摘要:能幫我們解決什么痛點實現異步執行,在未出現前,我們通常是使用嵌套的回調函數來解決的。那么,接下來我們看一下使用的實例可以傳入兩個參數表示兩個狀態的回調函數,第一個是,必選參數第二個是,可選參數的方便之處。
深入理解promise
對于現在的前端同學來說你不同promise你都不好意思出門了。對于前端同學來說promise已經成為了我們的必備技能。
那么,下面我們就來說一說promise是什么,它能幫助我們解決什么問題,我們應該如何使用它?
這是我個人對promise的理解。歡迎吐槽 :)
Promise是什么promise的意思是承諾,有的人翻譯為許愿,但它們代表的都是未實現的東西,等待我們接下來去實現。
Promise最早出現在commnjs,隨后形成了Promise/A規范。在Promise這個技術中它本身代表以目前還不能使用的對象,但可以在將來的某個時間點被調用。使用Promise我們可以用同步的方式寫異步代碼。其實Promise在實際的應用中往往起到代理的作用。例如,我們像我們發出請求調用服務器數據,由于網絡延時原因,我們此時無法調用到數據,我們可以接著執行其它任務,等到將來某個時間節點服務器響應數據到達客戶端,我們即可使用promise自帶的一個回調函數來處理數據。
Promise能幫我們解決什么痛點JavaScript實現異步執行,在Promise未出現前,我們通常是使用嵌套的回調函數來解決的。但是使用回調函數來解決異步問題,簡單還好說,但是如果問題比較復雜,我們將會面臨回調金字塔的問題(pyramid of Doom)。
var a = function() { console.log("a"); }; var b = function() { console.log("b"); }; var c = function() { for(var i=0;i<100;i++){ console.log("c") } }; a(b(c)); // 100個c -> b -> a
我們要桉順序的執行a,b,c三個函數,我們發現嵌套回調函數確實可以實現異步操作(在c函數中循環100次,發現確實是先輸出100個c,然后在輸出b,最后是a)。但是你發現沒這種實現可讀性極差,如果是幾十上百且回調函數異常復雜,那么代碼維護起來將更加麻煩。
那么,接下來我們看一下使用promise(promise的實例可以傳入兩個參數表示兩個狀態的回調函數,第一個是resolve,必選參數;第二個是reject,可選參數)的方便之處。
var promise = new Promise(function(resolve, reject){ console.log("............"); resolve(); // 這是promise的一個機制,只有promise實例的狀態變為resolved,才會會觸發then回調函數 }); promise.then(function(){ for(var i=0;i<100;i++) { console.log("c") } }) .then(function(){ console.log("b") }) .then(function(){ console.log("a") })
那么,為什么嵌套的回調函數這種JavaScript自帶實現異步機制不招人喜歡呢,因為它的可讀性差,可維護性差;另一方面就是我們熟悉了jQuery的鏈式調用。所以,相比起來我們會更喜歡Promise的風格。
promise的3種狀態上面提到了promise的 resolved 狀態,那么,我們就來說一下promise的3種狀態,未完成(unfulfilled)、完成(fulfilled)、失?。╢ailed)。
在promise中我們使用resolved代表fulfilled,使用rejected表示fail。
ES6的Promise有哪些特性promise的狀態只能從 未完成->完成, 未完成->失敗 且狀態不可逆轉。
promise的異步結果,只能在完成狀態時才能返回,而且我們在開發中是根據結果來選擇來選擇狀態的,然后根據狀態來選擇是否執行then()。
實例化的Promise內部會立即執行,then方法中的異步回調函數會在腳本中所有同步任務完成時才會執行。因此,promise的異步回調結果最后輸出。示例代碼如下:
var promise = new Promise(function(resolve, reject) { console.log("Promise instance"); resolve(); }); promise.then(function() { console.log("resolved result"); }); for(var i=0;i<100;i++) { console.log(i); /* Promise instance 1 2 3 ... 99 100 resolved result */
上面的代碼執行輸出結果的先后順序,曾經有人拿到這樣一個面試題問過我,所以,這個問題還是要注意的。
resolve中可以接受另一個promise實例resolve中接受另一個另一個對象的實例后,resolve本實例的返回狀態將會有被傳入的promise的返回狀態來取代。
reject狀態替換實例,代碼如下:
const p1 = new Promise(function (resolve, reject) { cosole.log("2秒之后,調用返回p1的reject給p2"); setTimeout(reject, 3000, new Error("fail")) }) const p2 = new Promise(function (resolve, reject) { cosole.log("1秒之后,調用p1"); setTimeout(() => resolve(p1), 1000) }) p2 .then(result => console.log(result)) .catch(error => console.log(error)) // fail
resolve狀態替換實例,代碼如下:
const p1 = new Promise(function (resolve, reject) { cosole.log("2秒之后,調用返回p1的resolve給p2"); setTimeout(resolve, 3000, "success") }) const p2 = new Promise(function (resolve, reject) { cosole.log("1秒之后,調用p1"); setTimeout(() => resolve(p1), 1000) }) p2 .then(result => console.log(result)) .catch(error => console.log(error)) // success
注意:promise實例內部的resolve也執行的是異步回調,所以不管resolve放的位置靠前還是靠后,都要等內部的同步函數執行完畢,才會執行resolve異步回調。
new Promise((resolve, reject) => { console.log(1); resolve(2); console.log(3); }).then(result => { console.log(result); }); /* 1 3 2 */
這個問題也在面試題中出現過,所以,要牢記。
promise和ajax如何結合使用function PromiseGet (url) { return new Promise( (resolve, reject) => { let xhr = new XMLHttpRequest() xhr.open("GET", url, true) xhr.onreadystatechange = function () { if (this.readyState === 4) { if (this.status === 200) { resolve(this.responseText, this) } else { let resJson = { code: this.status, response: this.response } reject(resJson, this) } } } xhr.send() }) }
我們發現用promise技術結合ajax,只是在promise實例中引入ajax,在ajax請求處理的結果中使用了resolve和reject狀態。
前面我們說了resolve(),返回執行then()代表完成,那么,reject()代表失敗,返回執行catch(),同時運行中拋出的錯誤也會執行catch()
現在有一個非常好用的promise和Ajax結合的github項目Axios ,想要深入了解的同學可以研究下它的源碼。
promise.all()方法可以處理一個以promise實例為元素的數組let promise = Promise.all([p1, p2, p3])
promise 的狀態由p1,p2,p3共同決定。當它們都為resolve狀態時,promise狀態為true,它們的返回值組成一個數組,傳遞給promise;它們只要有一個的狀態為reject,就將該實例的返回值傳遞給promise
promise.race()方法也可以處理一個promise實例數組但它和promise.all()不同,從字面意思上理解就是競速,那么理解起來上就簡單多了,也就是說在數組中的元素實例那個率先改變狀態,就向下傳遞誰的狀態和異步結果。
將一個普通對象轉化為Promise對象在開發中我們經常會遇到$.ajax()的使用且會遇到$.ajax()間的依賴使用,由于兩個或多個$.ajax()間是同步的,如果我們并排著寫實現不了依賴關系,所以,我們往往使用嵌套,但是對于ajax這樣復雜的結構,嵌套不是個好辦法,我們需要先將代碼抽象提取,做一下封裝,代碼如下:
/* url:地址 data:數據,在函數內部會轉化成json。如果沒傳,表示用GET方法;如果傳了,表示用POST方法 */ function ajax(url, data, callback) { $.ajax({ url: url, type: data == null ? "GET" : "POST", dataType: "json", data: data == null ? "" : JSON.stringify(data), async: true, contentType: "application/json", success: function (data) { callback(data); }, error: function (XMLHttpRequest, textStatus) { if (XMLHttpRequest.status == "401") { window.parent.location = "..."; self.location = "..."; } else { alert(XMLHttpRequest.responseText); } } }); }
那么,我們應該如何避免回調金字塔呢?很顯然,我們可以將它與promise結合,代碼如下:
function ajax(url, data, callback) => new Promise((resolve, reject) => { $.ajax({ url: url, type: data == null ? "GET" : "POST", dataType: "json", data: data == null ? "" : JSON.stringify(data), async: true, contentType: "application/json", success: function (data) { callback(data); resolve(); }, error: function (XMLHttpRequest, textStatus) { if (XMLHttpRequest.status == "401") { window.parent.location = "..." self.location = "..." } else { alert(XMLHttpRequest.responseText); } reject() } }) }) }
當然,這是不熟悉jQuery的同學,或者考慮長線Promise的,但是jQuery也為我們提供了按順序調用多個$.ajax()的方案,那就是deferred,它模擬了promise的實現,有興趣的同學可以查看源碼,看它是如何實現的。實例代碼如下:
$.ajax({ url:"./a" }).then(function(){ return $.ajax({ url:"./b" }); }).then(function(){ return $.ajax({ url:"./c" }); }).then(function(){ return $.ajax({ url:"./d" }); }).then(function(){ //TODO here });promise存在的問題
promise一旦執行,無法中途取消
promise的錯誤無法在外部被捕捉到,只能在內部進行預判處理
promise的內如何執行,監測起來很難
正是因為這些原因,ES7引入了更加靈活多變的async,await來處理異步。
這個稍后,后續可能還會繼續修改,也歡迎各位批評指正。有問題或者有其他想法的可以在我的GitHub上pr。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/107183.html
摘要:前言在學習前端的時候,我總是能聽到很多高級詞匯,比如今天會聊到的函數式編程高階函數。接下來我們看看,高階函數有可能會遇到的問題,又如何去解決。 前言 在學習前端的時候,我總是能聽到很多高級詞匯,比如今天會聊到的 函數式編程(Functional Programming) & 高階函數 (Higher-order function) 。但是當你真正的理解什么是 函數式編程 & 高階函數 ...
摘要:你能學到什么如何使用實現異步編程異步編程的原理解析前言結合上一篇文章,我們來聊聊基礎原理說到異步編程,你想到的是和,但那也只是的語法糖而已。表示一個異步操作的最終狀態完成或失敗,以及其返回的值。至此實現異步操作的控制。 你能學到什么 如何使用 Generator + Promise 實現異步編程 異步編程的原理解析 前言 結合 上一篇文章 ,我們來聊聊 Generator 基礎原理...
摘要:表示調用棧在下一將要執行的任務。兩方性能解藥我們一般有兩種方案突破上文提到的瓶頸將耗時高成本高易阻塞的長任務切片,分成子任務,并異步執行這樣一來,這些子任務會在不同的周期執行,進而主線程就可以在子任務間隙當中執行更新操作。 showImg(https://segmentfault.com/img/remote/1460000016008111); 性能一直以來是前端開發中非常重要的話題...
閱讀 1240·2021-11-15 11:37
閱讀 2252·2021-09-30 09:55
閱讀 4516·2021-09-22 15:51
閱讀 3749·2021-09-22 15:46
閱讀 2772·2019-08-30 15:52
閱讀 428·2019-08-29 16:20
閱讀 2895·2019-08-29 15:12
閱讀 1134·2019-08-26 18:27