問題
JavaScript的Callback機(jī)制深入人心俯萎。而ECMAScript的世界同樣充斥的各種異步操作(異步IO皮璧、setTimeout等)版述。異步和Callback的搭載很容易就衍生"回調(diào)金字塔"几蜻≈荆——由此產(chǎn)生Deferred/Promise肮雨。
Deferred起源于Python,后來被CommonJS挖掘并發(fā)揚(yáng)光大归敬,得到了大名鼎鼎的Promise酷含,并且已經(jīng)納入ECMAScript 6(JavaScript下一版本)。
Promise/Deferred是當(dāng)今最著名的異步模型汪茧,不僅強(qiáng)壯了JavaScript Event Loop(事件輪詢)機(jī)制下異步代碼的模型,同時增強(qiáng)了異步代碼的可靠性限番。
有了 Promise 舱污,就可以將異步操作以同步操作的流程表達(dá)出來,避免了層層嵌套的回調(diào)函數(shù)弥虐。此外扩灯,Promise 對象提供統(tǒng)一的接口,使得控制異步操作更加容易霜瘪。
概述
Promise 對象用于延遲(deferred) 計算和異步(asynchronous ) 計算珠插。一個Promise對象代表著一個還未完成,但預(yù)期將來會完成的操作颖对。
語法
new Promise(executor);
new Promise(function(resolve, reject) { ... });
例子:
<pre>
var promise = new Promise(function(resolve, reject) {
if (/** 異步操作成功* */){
resolve(value);
} else {
reject(error);
}
});
</br>
promise.then(function(value) {
// success
},
function(value) {
// failure
});
</pre>
參數(shù)
executor帶有 resolve捻撑、reject 兩個參數(shù)的函數(shù)對象。一旦我們的操作完成即可調(diào)用這些函數(shù)缤底。
- 第一個參數(shù)用在處理執(zhí)行成功的場景
- 第二個參數(shù)則用在處理執(zhí)行失敗的場景顾患。
描述
Promise對象是一個返回值的代理,這個返回值在promise對象創(chuàng)建時未必已知个唧。它允許你為異步操作的成功或失敗指定處理方法江解。 這使得異步方法可以像同步方法那樣返回值:異步方法會返回一個包含了原返回值的 promise 對象來替代原返回值。
Promise對象有以下幾種狀態(tài):
- pending: 初始狀態(tài), 非 fulfilled 或 rejected.
- fulfilled: 成功的操作.
- rejected: 失敗的操作.
pending狀態(tài)的promise對象既可轉(zhuǎn)換為帶著一個成功值的fulfilled 狀態(tài)徙歼,也可變?yōu)閹е粋€失敗信息的 rejected 狀態(tài)犁河。
特點
對象的狀態(tài)不受外界影響鳖枕,只有異步操作的結(jié)果,可以決定當(dāng)前是哪一種狀態(tài)桨螺,任何其他操作都無法改變這個狀態(tài)耕魄。
當(dāng)狀態(tài)發(fā)生轉(zhuǎn)換時,promise.then綁定的方法(函數(shù)句柄)就會被調(diào)用彭谁。一旦狀態(tài)改變吸奴,就不會再變,任何時候都可以得到這個結(jié)果缠局。這與事件(Event)完全不同则奥,事件的特點是,如果你錯過了它狭园,再去監(jiān)聽读处,是得不到結(jié)果的。
當(dāng)綁定方法時唱矛,如果 promise對象已經(jīng)處于 fulfilled 或 rejected 狀態(tài)罚舱,那么相應(yīng)的方法將會被立刻調(diào)用, 所以在異步操作的完成情況和它的綁定方法之間不存在競爭條件绎谦。
因為Promise.prototype.then
和 Promise.prototype.catch
方法返回 promises對象, 所以它們可以被鏈?zhǔn)秸{(diào)用—— 一種被稱為 composition 的操作管闷。
注意: 如果一個promise對象處在fulfilled或rejected狀態(tài)而不是pending狀態(tài),那么它也可以被稱為settled狀態(tài)窃肠。你可能也會聽到一個術(shù)語resolved 包个,它表示promise對象處于settled狀態(tài),或者promise對象被鎖定在了調(diào)用鏈中冤留。關(guān)于promise的狀態(tài)碧囊, Domenic Denicola 的 States and fates 有更多詳情可供參考。
基本的api
- Promise.resolve()
- Promise.reject()
- Promise.prototype.then()
- Promise.prototype.catch()
- Promise.all() 完成全部
- Promise.race() 完成一個
常用的JavaScript的promise的寫法
<pre>
function get(uri){
return http(uri, 'GET', null);
}
</br>
function post(uri,data){
if(typeof data === 'object' && !(data instanceof String || (FormData && data instanceof FormData))) {
var params = [];
for(var p in data) {
if(data[p] instanceof Array) {
for(var i = 0; i < data[p].length; i++) {
params.push(encodeURIComponent(p) + '[]=' + encodeURIComponent(data[p][i]));
}
} else {
params.push(encodeURIComponent(p) + '=' + encodeURIComponent(data[p]));
}
}
data = params.join('&');
}
return http(uri, 'POST', data || null, {
"Content-type":"application/x-www-form-urlencoded"
});
}
</br>
function http(uri,method,data,headers){
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open(method,uri,true);
if(headers) {
for(var p in headers) {
xhr.setRequestHeader(p, headers[p]);
}
}
xhr.addEventListener('readystatechange',function(e){
if(xhr.readyState === 4) {
if(String(xhr.status).match(/^2\d\d$/)) {
resolve(xhr.responseText);
} else {
reject(xhr);
}
}
});
xhr.send(data);
})
}
</br>
function wait(duration){
return new Promise(function(resolve, reject) {
setTimeout(resolve,duration);
})
}
</br>
function waitFor(element,event,useCapture){
return new Promise(function(resolve, reject) {
element.addEventListener(event,function listener(event){
resolve(event)
this.removeEventListener(event, listener, useCapture);
},useCapture)
})
}
</br>
function loadImage(src) {
return new Promise(function(resolve, reject) {
var image = new Image;
image.addEventListener('load',function listener() {
resolve(image);
this.removeEventListener('load', listener, useCapture);
});
image.src = src;
image.addEventListener('error',reject);
})
}
</br>
function runScript(src) {
return new Promise(function(resolve, reject) {
var script = document.createElement('script');
script.src = src;
script.addEventListener('load',resolve);
script.addEventListener('error',reject);
(document.getElementsByTagName('head')[0] || document.body || document.documentElement).appendChild(script);
})
}
</br>
function domReady() {
return new Promise(function(resolve, reject) {
if(document.readyState === 'complete') {
resolve();
} else {
document.addEventListener('DOMContentLoaded',resolve);
}
})
}
</pre>
兼容性墊片polyfill of the ES6 Promise
參考資料: