一文講明白Promise

zekun發表於2018-09-30
  • Promise物件的特點

    • 物件不受外界影響

    • 一旦狀態改變,就不會再變

    • Promise返回的永遠是一個Promise物件,傳遞值只能通過resolve和reject

    • 注意,呼叫resolvereject並不會終結 Promise 的引數函式的執行。

      new Promise((resolve, reject) => {
        resolve(1);
        console.log(2);
      }).then(r => {
        console.log(r);
      });
      // 2
      // 1
      複製程式碼
      • 所以正確最好這麼寫:

        new Promise((resolve, reject) => {
          return resolve(1);
          // 後面的語句不會執行
          console.log(2);
        })
        複製程式碼
  • 完整呼叫形式

    function p(){
        return new Promise((resolve,reject) =>{
            // todo
        }).then(result =>{
            console.log(result)
        },reject  =>{
            console.log('reject'+reject)
        }).catch(e){
            // to catch error
        }
    }
    複製程式碼
  • 關於then,catch:他們都返回promise物件,因為所有的promise返回的都是一個promise,所以promise才可以鏈式呼叫。

  • Then方法:

    • Promise 例項具有then方法,也就是說,then方法是定義在原型物件Promise.prototype上的。
    • then方法返回的是一個新的Promise例項(注意,不是原來那個Promise例項)。因此可以採用鏈式寫法,即then方法後面再呼叫另一個then方法。
    • then方法可以接受兩個回撥函式作為引數。第一個回撥函式是Promise物件的狀態變為resolved時呼叫,第二個回撥函式是Promise物件的狀態變為rejected時呼叫。其中,第二個函式是可選的,不一定要提供。這兩個函式都接受Promise物件傳出的值作為引數。
    • 如果呼叫resolve函式和reject函式時帶有引數,那麼它們的引數會被傳遞給then中的回撥函式。reject函式的引數通常是Error物件的例項,表示丟擲的錯誤;resolve函式的引數除了正常的值以外,還可能是另一個 Promise 例項,比如像下面這樣。
  • Catch方法

    • Promise.prototype.catch方法是.then(null, rejection)的別名,用於指定發生錯誤時的回撥函式。

    • Promise 物件的錯誤具有“冒泡”性質,會一直向後傳遞,直到被捕獲為止。也就是說,錯誤總是會被下一個catch語句捕獲。

      • 所以不要在then方法裡面定義 Reject 狀態的回撥函式(即then的第二個引數),而是在Promise呼叫鏈的結尾使用catch方法。

        // bad
        promise
          .then(function(data) {
            // success
          }, function(err) {
            // error
          });
        
        // good
        promise
          .then(function(data) { //cb
            // success
          })
          .catch(function(err) {
            // error
          });
        複製程式碼
    • 跟傳統的try/catch程式碼塊不同的是,如果沒有使用catch方法指定錯誤處理的回撥函式,Promise 物件丟擲的錯誤不會傳遞到外層程式碼,即不會有任何反應。但是在Node裡有一個unhandledRejection事件,專門監聽未捕獲的reject錯誤

      process.on('unhandledRejection', function (err, p) {
        throw err;
      });
      複製程式碼
      • 注意,Node 有計劃在未來廢除unhandledRejection事件。如果 Promise 內部有未捕獲的錯誤,會直接終止程式,並且程式的退出碼不為 0。
  • Promise.all([這是若干個promise組成的陣列])

    • const p = Promise.all([p1, p2, p3]);
      複製程式碼
    • Promise.all方法接受一個陣列作為引數,p1p2p3都是 Promise 例項,如果不是,就會先呼叫下面講到的Promise.resolve方法,將引數轉為 Promise 例項,再進一步處理。(Promise.all方法的引數可以不是陣列,但必須具有 Iterator(遍歷) 介面,且返回的每個成員都是 Promise 例項。)

    • 只有p1p2p3的狀態都變成fulfilledp的狀態才會變成fulfilled,此時p1p2p3的返回值組成一個陣列,傳遞給p的回撥函式。

      只要p1p2p3之中有一個被rejectedp的狀態就變成rejected,此時第一個被reject的例項的返回值,會傳遞給p的回撥函式。

  • Promise.race()

    • 其實和Promise.all()差不多,就是p1,p2,p3有一個狀態變成resolve,p就變成resolve然後傳遞值
  • Promise.resolve()

    • 有時需要將現有物件轉為 Promise 物件,Promise.resolve方法就起到這個作用。這個方法的引數分為4中情況

      • 引數是一個 Promise 例項

        • 直接返回
      • 引數是一個thenable物件,指的是具有then方法的物件,比如下面這個物件。

        let thenable = {
          then: function(resolve, reject) {
            resolve(42);
          }
        };
        
        // Promise.resolve方法會將這個物件轉為 Promise 物件
        // 然後就立即執行thenable物件的then方法。
        
        //上面程式碼中,thenable物件的then方法執行後,物件p1的狀態就變為resolved
        // 從而立即執行最後那個then方法指定的回撥函式,輸出 42。
        複製程式碼
      • 引數不是具有then方法的物件,或根本就不是物件

        • 如果引數是一個原始值,或者是一個不具有then方法的物件,則Promise.resolve方法返回一個新的 Promise 物件,狀態為resolved

          const p = Promise.resolve('Hello');
          
          p.then(function (s){
            console.log(s)
          });
          // Hello
          
          //上面程式碼生成一個新的 Promise 物件的例項p。由於字串Hello不屬於非同步操作(判斷方法是字串物件不具有 then 方法),返回 Promise 例項的狀態從一生成就是resolved,所以回撥函式會立即執行。Promise.resolve方法的引數,會同時傳給回撥函式。
          複製程式碼
      • 不帶有任何引數Promise.resolve方法允許呼叫時不帶引數,直接返回一個resolved狀態的 Promise 物件。

    • 所以,如果希望得到一個 Promise 物件,比較方便的方法就是直接呼叫Promise.resolve方法。

  • Promise.reject()和resolve()方法

  • Generator函式與Promise的結合

    • 大概意思就是在我們使用generator的時候,遇到yield一個非同步操作,一般返回的是promise物件

相關文章