本文同步自我的個人博客:http://www.52cik.com/2015/11/08/promise.html
JavaScript 的 promises 事實標准稱為 Promises/A+。ES6 的 Promise API 便遵循這個標准。
promises 的英文直譯是“承諾,諾言”。但是在張鑫旭大神的《ES6 JavaScript Promise的感性認知》文章里得知,
而是應該根據其音譯——“普羅米修斯”,來翻譯。普羅米修斯是希臘神話故事中的英雄,名字的意思是“先知”。
回調地獄
好了,我就不講那些學術化的東西了,直接進入正題,來個實際的例子比什么都強。
例如我們有3個 ajax 請求,需要按照順序加載。
$.get("/url1", function (data) {
// 一些處理
$.get("/url2", function (data) {
// 一些處理
$.get("/url3", function (data) {
// 最終處理
});
});
});
非常熟悉的畫面,那如果請求增加到10個,甚至更多,會發生什么呢?
這幅圖非常直觀形象的展示了回調地獄。
時間支配 - 按順序調度
為了避免回調地獄的發生,就需要 Promise 出馬來合理的支配我們的任務時間。
首先需要學習下第一個 api,具體參閱 MDN Promise 文檔。
其實非常簡單,就是用 Promise 包裝下 ajax 請求即可,代碼如下。
// 用 Promise 包裝 get 請求
function myGet(url) {
return new Promise(function(resolve, reject) {
$.get(url, resolve);
});
}
myGet('/url1').then(function (data) {
// 一些處理,data為返回值
return myGet('/url2');
}).then(function (data) {
// 一些處理
return myGet('/url3');
}).then(function (data) {
// 最終處理
});
這樣就可以按順序寫 ajax 調用了,不會陷入無盡的回調之中。
如果你熟悉 jquery,那么你一定知道 Deferred,它實現了類似 Promise 的東西。
$.get("/url1").then(function (data) {
// 一些處理,data為返回值
return $.get("/url2");
}).then(function (data) {
// 一些處理
return $.get("/url3");
}).then(function (data) {
// 最終處理
});
這樣看着簡潔多了,雖然 Deferred 和 Promise 實現方法不一樣,但是他們的結果是一致的。
時間支配 - 異步並發
大家都知道 ajax 異步請求是沒有返回順序的,發了3個請求,有可能是1,2,3的返回,也有可能是3,1,2的返回順序。
那我不想按順序請求,而是並發請求,但是在最終所有請求完成后執行一段操作。
比如頁面所有數據都加載完后隱藏 loading 層。
這個時候,需要用到一個新方法 Promise.all,其實很簡單,它接受一個 promise 數組最為參數。
function myGet(url) {
return new Promise(function(resolve, reject) {
$.get(url, resolve);
});
}
var arr = [];
arr.push(myGet('/url1'));
arr.push(myGet('/url2'));
arr.push(myGet('/url3'));
Promise.all(arr).then(function (datas) {
// 最終處理,datas 是所有返回結果的數組
console.log("返回數據為", datas);
});
感覺比剛才的按順序還要清爽,有木有。其實 jquery 也有類似的功能,而且用起來超爽的。
$.when($.get('/url1'), $.get('/url2'), $.get('/url3'))
.then(function (data1, data2, data3) {
// 最終處理
console.log("返回數據為", data1, data2, data3);
});
雖然形式有點怪異,不過調整下就OK了。
var arr = [];
arr.push($.get('/url1'));
arr.push($.get('/url2'));
arr.push($.get('/url3'));
$.when.apply(null, arr).then(function () {
// 最終處理
console.log("返回數據為", arguments);
});
利用 apply 來展開參數,代碼看着舒服多了,跟上面的 Promise.all 是一樣一樣的。
結束語
本文本是我在一邊學習測試一邊寫的,例子都跑過OK的,通篇下來后,對 Deferred 和 Promise 有了一定認識,
雖然並不一定透徹,至少比起以前好多了。
為什么叫它為 時間支配者 呢,一開始沒這樣的想法,寫到一半的時候發現確實可以合理的支配異步的時間調度問題。
異步操作最大的特點就是時間的不確定性,所以正常情況下很難流暢的書寫簡單而有邏輯性的代碼。
比如按順序的異步,雖然可以合理的回調,但是會陷入回調地獄,再如並發異步呢?
並發異步,也許你要設置一個全局變量來記次,最后一個完成的,要觸發下完成后的任務。
這樣雖然也可以實現,但是代碼會顯的臃腫而又凌亂。
但是有了 Promise 的幫助,這一切變的這么簡單,自然。