ES6中的Promise深入解析

promise主要是爲了解決回調帶來的瘋狂行爲, Promise 風暴已經開始席捲JavaScript 世 界。

實際上,絕大多數 JavaScript/DOM 平臺新增的異步 API 都是基於 Promise 構建的。


一. 異步編程的背景

JavaScript 的引擎建立在單線程事件輪詢(single-threaded event loop)概念之上。

單線程意味着一段時間內只能執行一段代碼,與 Java 和 C++ 這些允許多段代碼同時執行的多線程語言形成了鮮明對比。

JavaScript 引擎在相同的時間內只能執行一段代碼.

當代碼由 JavaScript 引擎執行完畢後,引擎通過 event loop 找到並執行隊列中的下一個任務。

event loop 是 JavaScript 引擎內部的線程用來監控代碼的執行情況和管理任務隊列。

需要牢記的是既然它是個隊列,那麼任務就會由開始到最後的順序依次執行。


二. Promise出來之前的兩種異步編程方式

  1. 事件模型

不夠靈活.

  1. 回調模式

回調地獄


三. promise基礎

一個 Promise 就是一個代表了異步操作最終完成或者失敗的對象。

可以把Promise理解成對將來的一個”承諾”. 這個承諾不管將來如何一定會有一個結果:要麼兌現承諾, 要麼不兌現. 不管結果是什麼, 都會對被承諾者一個交代.

我們通常有兩種方式來獲取到一個Promise對象:

  1. 使用Promise構造函數來創建Promise對象

  2. 調用其他的一些api返回一個Promise對象.(其實是這些api內部創建並返回了Promise對象)

我們先用第一種方式來深入的理解下promise.


promise 的生命週期(The Promise Lifecycle)

  1. 每個 promise 的生命週期一開始都會處於短暫的掛起(pending)狀態,表示異步操作仍未完成,即掛起的 promise 被認定是未定的(unsettled)。

  2. 一旦異步操作完成,promise 就被認爲是已定(settled)的並處於以下的兩種狀態之一:

    • fulfilled(resolved): promise 的異步操作已完成。
    • rejected: promise 的異步操作未完成,原因可能是發生了錯誤或其它理由。

創建Promise對象並添加異步任務

使用構造函數Promise來創建Promise對象.

需要接收一個執行函數(executor), 這個函數內包含了初始化Promise的一些代碼.
該執行函數接收 resolve 和 reject 兩個參數(其實是兩個函數)。
- resolve 函數會在執行函數成功運行後發出信號表示該 promise 已經可用,
- reject 函數代表改執行函數運行失敗。

/*
對setTimeout 用promise作封裝

創建出來一個未定的promise

注意:
1. 創建Promise對象的時候, 執行函數的內的代碼會立即執行.
2. 當 resolve() 和 reject() 在執行函數內部被調用後,
爲了處理這個 promise,一個任務會被放置到任務隊列中。
該種行爲被稱爲任務調度(job scheduling)
*/

var count = 1;
let promise = new Promise(function (resolve, reject){
if(count == 1){
resolve("成功了");
}else{
reject("失敗了");
}
});

/*添加異步任務
* then需要兩個函數:
* 函數1:異步操作成功之後的回調函數
* 函數2:異步操作失敗之後的回調函數
* */
promise.then(function (value){
console.log(value);
}, function (value){
console.log(value)
});

注意:

  • Promise的意思是承諾,保證. 他帶來了哪些保證呢?

  • 在JavaScript事件隊列的當前運行完成之前,回調函數永遠不會被調用。

  • 通過 .then 形式添加的回調函數,甚至都在異步操作完成之後才被添加的函數,都會被調用
  • 通過多次調用 .then,可以添加多個回調函數,它們會按照插入順序並且獨立運行。

四. Promise鏈式調用

一個常見的需求就是連續執行兩個或者多個異步操作,這種情況下,每一個後來的操作都在前面的操作執行成功之後,帶着上一步操作所返回的結果開始執行。

我們可以通過創造一個promise chain來完成這種需求。

這也是Promise的神奇之處.

是因爲: 每次調用then方法會返回一個全新的Promise對象:


promise.then(function (value){
console.log(value);
}, function (value){
console.log(value)
}).then(function (){
console.log("哈哈哈")
})

在鏈中傳遞數據


promise.then(function (value){
console.log(value);
return value + "1"; // 給後面的異步任務傳遞數據
}, function (value){
console.log(value)
}).then(function (value){
console.log("哈哈哈", value)
})

五. 捕獲錯誤

通過前面的學習我們已經知道, then的第二個參數是用來處理錯誤.

其實我們可以省略這個參數不處理錯誤.

省略第二個參數:

promise.then(function (value){
console.log(value);
return value + "1";
})

也可以使用catch來專門來處理錯誤:


promise.then(function (value){
console.log(value);
}).catch(function (value){
console.log(value);
})

// 與下面完全等價:
promise.then(function (value){
console.log(value);
}, function (value){
console.log(value);
console.log(a);
})

注意:

  • catch也會返回一個新的promise對象

六. 關於then返回的promise

好好體會

then方法總是返回一個新的Promise,而它的行爲與then中的回調函數的返回值有關:

  • 如果then中的回調函數返回一個值,那麼then返回的Promise將會成爲接受狀態,並且將返回的值作爲接受狀態的回調函數的參數值。

  • 如果then中的回調函數拋出一個錯誤,那麼then返回的Promise將會成爲拒絕狀態,並且將拋出的錯誤作爲拒絕狀態的回調函數的參數值。

  • 如果 then 中的回調函數返回的是一個 promise A, 那麼then返回的promise B的狀態始終會與 promise A 保持一致. 並且回調函數的參數值也始終相同.

  • promise A的回調函數的參數也始終與 promise B的回調函數的參數保持一致.

使用promise封裝ajax

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章