1. 程式人生 > >淺談promise(then、catch、resolve、reject、race、all、done、finally)

淺談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
  1. 使用new Promise方法建立promise物件。
  2. .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 })
  );
};

END