promise主要是爲了解決回調帶來的瘋狂行爲, Promise 風暴已經開始席捲JavaScript 世 界。
實際上,絕大多數 JavaScript/DOM 平臺新增的異步 API 都是基於 Promise 構建的。
一. 異步編程的背景
JavaScript 的引擎建立在單線程事件輪詢(single-threaded event loop)概念之上。
單線程意味着一段時間內只能執行一段代碼,與 Java 和 C++ 這些允許多段代碼同時執行的多線程語言形成了鮮明對比。
JavaScript 引擎在相同的時間內只能執行一段代碼.
當代碼由 JavaScript 引擎執行完畢後,引擎通過 event loop 找到並執行隊列中的下一個任務。
event loop 是 JavaScript 引擎內部的線程用來監控代碼的執行情況和管理任務隊列。
需要牢記的是既然它是個隊列,那麼任務就會由開始到最後的順序依次執行。
二. Promise出來之前的兩種異步編程方式
- 事件模型
不夠靈活.
- 回調模式
回調地獄
三. promise基礎
一個 Promise 就是一個代表了異步操作最終完成或者失敗的對象。
可以把Promise理解成對將來的一個”承諾”. 這個承諾不管將來如何一定會有一個結果:要麼兌現承諾, 要麼不兌現. 不管結果是什麼, 都會對被承諾者一個交代.
我們通常有兩種方式來獲取到一個Promise
對象:
使用
Promise
構造函數來創建Promise
對象調用其他的一些
api
返回一個Promise
對象.(其實是這些api
內部創建並返回了Promise
對象)
我們先用第一種方式來深入的理解下promise
.
promise
的生命週期(The Promise Lifecycle)
每個 promise 的生命週期一開始都會處於短暫的掛起(pending)狀態,表示異步操作仍未完成,即掛起的 promise 被認定是未定的(unsettled)。
一旦異步操作完成,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