十分鐘帶你解讀什麼是Promise非同步程式設計
Promise的含義
Promise是非同步程式設計的一種解決方案,比傳統的解決方案--回撥函式和事件--更合理且更強大。
Promise物件具有以下兩個特點:
- 物件的狀態不受外界影響。Promise物件代表一個非同步操作,有3中狀態:Pending(進行中)、Fulfilled(已成功)和Rejected(已失敗)。
- 一旦狀態改變就不會再變,任何時候都可以得到這個結果。
Promise也有一些缺點:
- 無法取消Promise,一旦新建它就會立即執行,無法中途取消。
- 如果不設定回撥函式,Promise內部丟擲的錯誤不會反應奧外部。
- 當處於Pending狀態時,無法得知目前進展到哪一個階段(剛開始還是即將完成)。
基本用法
ES6規定,Promise物件是一個建構函式,用來生成Promise例項。
var promise = new Promise(function(resolve,reject) {
// ...some code
If( /*非同步操作成功*/){
resolve(value);
}else{
reject(error);
}
});
Promise建構函式接受一個函式作為引數,該函式的兩個引數分別是resolve和reject。這兩個函式有JavaScript引擎提供,不需要自己部署。
resolve
Promise.prototype.then()
Promise例項生成以後,可以用then方法分別指定Resolved狀態和Rejected狀態的回撥函式。
promise.then(function(value){
//成功
},function(error){
//失敗
});
then方法接受兩個回撥函式作為引數。第一個回撥函式是Promise物件的狀態變為成功時呼叫,是必填的。第二個是Promise物件的狀態變為失敗時呼叫,是選填的。
Promise.prototype.catch()
Promise.prototype.catch()方法是.then(null,rejection)的別名,用於發生失敗時的回撥函式。
如果非同步操作丟擲錯誤,狀態就會變為Rejected,然後就會呼叫catch方法指定的回撥函式處理這個錯誤。
promise.then( (val) => {......} )
.catch( error) => {......} );
//等價於:
promise.then( (val) => {......}, (error) => {......} )
另外,then方法指定的回撥函式如果在執行中丟擲錯誤,也會被catch方法捕獲。
promise.then( (val) => {
trow new error(“此錯誤也會被catch捕獲到”);
} ).catch(
(error) => {......}
);
catch方法返回的還是一個Promise物件,因此後面還可以接著條用then方法。
promise.then( (val) => {
......
} ).catch(
(error) => {......}
).then(
(value2) => { ...... }
);
如果後面的then方法裡面報錯,就與前面的catch無關了。
Promise.all()
Promise.all方法用於將多個Promise例項包裝成一個新的Promise例項。
var p = Promise.all( [ p1, p2, p3] );
Promise.all方法接受一個數組作為引數(不一定是陣列,但必須是具有Iterator介面,且返回的每個成員都是Promise例項),p1, p2, p3都是Promise物件的例項。
P的狀態由p1、 p2、 p3決定,分為兩種情況:
- 只有p1、 p2、 p3的狀態都變成Fulfilled,p的狀態才會變成Fulfilled,此時p1、 p2、 p3的返回值組成一個數組,傳遞給p的回撥函式。
- 只要p1、 p2、 p3中有一個被Rejected,p的裝填就變成Rejected,此時第一個被Rejected的例項返回值會傳遞給p的回撥函式。
如果作為引數的Promise例項自身定義了catch方法,那麼它被rejected時並不會觸發Promise.all()的catch方法。如下面的例子:
const p1 = new Promise((resolve,reject) => {
resolve(‘hello’);
})
.then(result => result)
.catch(e => e);
const p2 = new Promise((resolve,reject) => {
throw new error(“報錯了”);
})
.then(result => result)
.catch(e => e);
Promise.all([p1, p2])
.then( result => console.log(result ) )
.catch(e => console.log(“e”));
//[ ‘hello’, Error:報錯了]
如果去掉p2的catch方法則會執行Promise.all的catch方法。
const p1 = new Promise((resolve,reject) => {
resolve(‘hello’);
})
.then(result => result);
const p2 = new Promise((resolve,reject) => {
throw new error(“報錯了”);
})
.then(result => result)
.catch(e => e);
Promise.all([p1, p2])
.then( result => console.log(result ) )
.catch(e => console.log(“e”));
// Error:報錯了
Promise.race()
Promise.all方法同樣是將多個Promise例項包裝成一個新的Promise例項。
var p = Promise.race( [ p1, p2, p3] );
與 Promise.all不同的是,只要p1、 p2、 p3中有一個率先改變狀態,P的狀態就跟著改變。
Promise.resolved()
將現有的物件轉化為Promise物件。
Promise.resolve(‘foo’);
//等價於
new Promise(result => result(“foo”))
Promise.reject()
返回一個新的Promise例項,狀態為Rejected。
var p = Promise.reject(“出錯了”);
//等同於
var p = new Promise(( resolve, reject) => reject(“出錯了”));
兩個有用的附加方法
done()
無論Promise物件的回撥鏈以then方法還是catch方法結束,只要最後一個方法丟擲錯誤,都有可能無法捕捉到。因此done方法就派上了用場,它總處於回撥鏈的尾端,保證任何可能丟擲的錯誤都會被捕捉到。
asyncFunc()
.then()
.catch()
.then()
.done();
finally()
Finally方法用於指定不管Promise物件最後狀態如何都會執行的操作。它與done方法的最大區別在於,它接受一個普通的回撥函式作為引數,該函式不管則麼樣都必須執行。
asyncFunc()
.then()
.catch()
.finally();
應用
載入圖片
const preloadImage = function (path) {
return new Promise(
(resolve, reject) => {
var img = new Image();
img.onload = () => resolve("成功");
img.onerror = () => reject("失敗");
img.src = path;
}
).then(
value => {console.log(value) //非同步載入成功則呼叫該方法
}).catch(
value => console.log(value) //非同步載入失敗則呼叫該方法
);