淺談promise(then、catch、resolve、reject、race、all、done、finally)
抽象非同步處理物件以及對其進行各種操作的元件
主要型別
Constructor
建構函式Instance Method
例項方法Static Method
靜態方法
**promise.then(onFulfilled, onRejected)
操作之後執行回撥函式。**Fulfilled為執行成功呼叫的函式,onRejected為執行失敗呼叫的函式。
promise.catch(onRejected)
捕獲到錯誤執行onRejected函式
promise狀態
- 只能由pending到fulfilled或Rejected
- 狀態不可逆
graph LR
Pending -->Fulfilled
Pending-->Rejected
- 使用
new Promise
方法建立promise物件。 - 用
.then
或.then
新增promise物件的處理函式。
Promise.resolve/Promise.reject
靜態方法Promise.resolve(value) 可以認為是 new Promise() 方法的快捷方式。
new Promise(function(resolve){
resolve(42);
});
promise的鏈式寫法(chain)
function taskA() {
console.log("Task A" );
}
function taskB() {
console.log("Task B");
}
function onRejected(error) {
console.log("Catch Error: A or B", error);
}
function finalTask() {
console.log("Final Task");
}
var promise = Promise.resolve();
promise
.then(taskA)
.then(taskB)
.catch(onRejected)
.then(finalTask);
- then 註冊onFulfilled時的回撥函式
- catch 註冊onRejected時的回撥函式
鏈式寫法的引數傳遞
function doubleUp(value) {
return value * 2;
}
function increment(value) {
return value + 1;
}
function output(value) {
console.log(value);// => (1 + 1) * 2
}
var promise = Promise.resolve(1);
promise
.then(increment)
.then(doubleUp)
.then(output)
.catch(function(error){
// promise chain中出現異常的時候會被呼叫
console.error(error);
});
promise.all
Promise.all
接收一個 promise物件的陣列作為引數,當這個數組裡的所有promise物件全部變為resolve或reject狀態的時候,它才會去呼叫 .then
方法。
Promise.all([request.comment(), request.people()]);
request.comment()
與request.people()
會同時執行(並行執行)。- promise的結果和傳遞給Promise.all的陣列的順序一致。
promise.race
Promise.race
只要有一個promise物件進入 FulFilled 或者 Rejected 狀態的話,就會繼續進行後面的處理。
注:promise.race在第一個函式執行完畢後,並不會取消其他函式的執行狀態,但是其餘函式執行完畢之後不會再呼叫.then
。
then與catch
.catch
也可以理解為 promise.then(undefined, onRejected),但實際使用中我們還是會將then與catch分開使用。
建議多用catch方法,少用then方法的第二個引數
function throwError(value) {
// 丟擲異常
throw new Error(value);
}
// <1> onRejected不會被呼叫
function badMain(onRejected) {
return Promise.resolve(42).then(throwError, onRejected);
}
// <2> 有異常發生時onRejected會被呼叫
function goodMain(onRejected) {
return Promise.resolve(42).then(throwError).catch(onRejected);
}
// 執行示例
badMain(function(){
console.log("BAD");
});
goodMain(function(){
console.log("GOOD");
});
//輸出結果
`GOOD`
- 使用
.then
即使 throwError 丟擲了異常,onRejected 指定的函式也不會被呼叫。 - 使用
.catch
因為是鏈式操作,會捕獲到上一步.then
中丟擲的操作。
promise中的錯誤捕獲
const someAsyncThing = function() {
return new Promise(function(resolve, reject) {
// 下面一行會報錯,因為x沒有宣告
resolve(x + 2);
});
};
someAsyncThing().then(function() {
console.log('everything is great');
});
setTimeout(() => { console.log(123) }, 2000);
// Uncaught (in promise) ReferenceError: x is not defined
// 123
- 以上程式碼中,someAsyncThing函式中會出現一個變數為定義的錯誤,然而因為錯誤發生在promise物件中,這個錯誤不會被監聽到,promise之後的程式碼也會繼續執行
- 也就是說promise內部的錯誤不會影響promise外部的程式碼
- 要想監聽這個錯誤可使用node的unhandledRejection事件
- unhandledRejection事件有兩個引數
- 第一個為錯誤物件
- 第二個為報錯的Promise例項
process.on('unhandledRejection',function (err, p) {
throw err;
});
Promise.resolve
有時需要將現有物件轉為 Promise 物件,Promise.resolve方法就起到這個作用。
Promise.resolve一般有四種用法
- 引數是Promise例項,將直接返回這個例項
- 引數是個
thenable
物件,(thenable物件指的是具有then方法的物件,程式碼如下),會將這個物件轉為 Promise 物件然後立即執行它的then方法
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};
- 如果引數是一個原始值,或者是一個不具有then方法的物件,則Promise.resolve方法返回一個狀態為resolved的新 Promise 物件。
const jsPromise = Promise.resolve($.ajax('/whatever.json'));
- Promise.resolve方法允許呼叫時不帶引數,直接返回一個resolved狀態的 Promise 物件。
done()
Promise 物件的回撥鏈,不管以then方法或catch方法結尾,要是最後一個方法丟擲錯誤,都有可能無法捕捉到(因為 Promise 內部的錯誤不會冒泡到全域性)。因此,我們可以提供一個done方法,總是處於回撥鏈的尾端,保證丟擲任何可能出現的錯誤。
- 程式碼示例:
asyncFunc()
.then(f1)
.catch(r1)
.then(f2)
.done();
- 程式碼實現
Promise.prototype.done = function (onFulfilled, onRejected) {
this.then(onFulfilled, onRejected)
.catch(function (reason) {
// 丟擲一個全域性錯誤
setTimeout(() => { throw reason }, 0);
});
};
done都會捕捉到任何可能出現的錯誤,並向全域性丟擲。
finally()
finally方法用於指定不管 Promise 物件最後狀態如何,都會執行的操作。只要proise物件出現最後狀態,finally方法就一定會執行。
類似於一個函式的回撥。
- 程式碼示例
server.listen(0)
.then(function () {
// run test
})
.finally(server.stop);
- 程式碼實現
Promise.prototype.finally = function (callback) {
let P = this.constructor;
return this.then(
value => P.resolve(callback()).then(() => value),
reason => P.resolve(callback()).then(() => { throw reason })
);
};