ES6 Promise

參考
https://pouchdb.com/2015/05/1...
https://developer.mozilla.org...
http://es6.ruanyifeng.com/#do...

Promises

是一種編寫異步代碼的方法。

Promise 對象

用於表示一個異步操作的最終狀態(完成或失敗),以及該異步操作的結果值。

Promise 使用

Promise適用於這樣的場景,後面的操作必須根據前面的操作的結果做出相應的反應。 那麼後面的操作必須等前面的操作完成並且獲得前面操作的結果。

假設我們現在有三個操作doSomethingFirst,doSomethingSecond 和finalHandler。
doSomethingSecond 需要根據 doSomethingFirst 的結果做出反應
finalHandler 需要根據 doSomethingSecond 的結果做出反應
流程如下:

//resOfDoSomethingFirst 是 DoSomethingFirst的結果
  doSomethingFirst
|-----------------|
                  doSomethingSecond(resOfDoSomethingFirst)
                  |------------------|
                                     finalHandler(doSomethingSecond的結果)
                                     |------------------|
實現這個場景需要解決以下兩個問題:
    後面的操作 如何知道 前面的操作 完成了
    後面的操作 如何知道 前面的操作 的執行結果是什麼

//包裝第一個操作在 doSomethingFirst 中
const doSomethingFirst = new Promise(function(resolve, reject) {
  // ... some code

  if (/*操作成功 */){
    resolve(doSomethingFirstValue);     //將doSomethingFirst對象的狀態從“pending”變爲“resolved”
  } else {
    reject(doSomethingFirstError);
  }
});

//包裝第二個操作在 
doSdoSomethingSecond(resOfDoSomethingFirst) {
      // ... some code
    return somePromise(); //返回一個promise 對象
});

//整個操作流程如下:
doSomethingFirst()
    .then(doSdoSomethingSecond)
    .then(finalHandler)
    .catch(function (err) {
      console.log(err);
    })
    .finally(() => {···});
    
通過new Promise創建的Promise 實例doSomethingFirst 有以下方法:
    Promise.all(iterable)
    Promise.race(iterable)
    Promise.reject(reason)
    Promise.resolve(value)
    Promise.prototype.catch(onRejected)
    Promise.prototype.then(onFulfilled, onRejected)
    Promise.prototype.finally(onFinally)

1. doSomethingSecond 如何知道 doSomethingFirst 操作完成了
    通過doSomethingFirst狀態的變更通知。

    一個 Promise有以下幾種狀態:
        pending: 操作未完成。
        fulfilled: 操作完成,並且成功。
        rejected: 操作完成,但是失敗。

    resolve()函數:
        在異步操作成功時調用
        作用是:將Promise對象的狀態從 pending 變爲 fulfilled,並將異步操作的結果,作爲參數傳遞出去;
    reject()函數:
        在異步操作失敗時調用
        作用是:將Promise對象的狀態從 pending 變爲 rejected, 並將異步操作的錯誤,作爲參數傳遞出去。

    當操作完成時(Promise對象的狀態變爲fulfilled 或 rejected時),doSomethingFirst 就會通過then()函數調用doSomethingSecond,doSomethingSecond就知道doSomethingFirst已經完成了。
2. doSomethingSecond 如何知道 doSomethingFirst 的執行結果是什麼
    doSomethingFirst通過給then()函數調用doSomethingSecond(resOfDoSomethingFirst)並把執行結果resOfDoSomethingFirst作爲參數傳遞給doSomethingSecond

Promise.prototype.then(onFulfilled, onRejected)

用於爲 Promise 實例添加狀態改變時的回調函數
返回值:一個新的 Promise,所可以採用鏈式寫法,then()函數後面再調用then()函數
參數:
    onFulfilled 
        是一個函數,有一個參數,用來記錄變成fulfilled狀態返回的結果
        當doSomethingFirst這個Promise的狀態變成fulfilled 時,onFulfilled作爲回調函數被調用
    onRejected
        是一個函數,有一個參數,用來記錄變成rejected狀態返回的原因
        當doSomethingFirst這個Promise的狀態變成rejected 時,onRejected作爲回調函數被調用

    在當前的例子裏參數onFulfilled就是doSomethingSecond(resOfDoSomethingFirst),resOfDoSomethingFirst 記錄了doSomethingFirst 變成fulfilled狀態返回的結果。

注意事項:
    當給then()傳入的參數不是函數時,它實際上將其解釋爲then(null),將使先前的Promise的結果落空
        Promise.resolve('foo').then(Promise.resolve('bar')).then(function (result) {
          console.log(result); // foo
        });    
            等價於
        Promise.resolve('foo').then(null).then(function (result) {
            console.log(result);// foo 
        });
    
        上面的代碼並沒有按照我們期望的打印"bar", 而是打印"foo"
        正確的寫法是:
        Promise.resolve('foo').then(function () {
          return Promise.resolve('bar');
        }).then(function (result) {
          console.log(result); // bar
        });


給then()傳入的參數是函數時,函數的內部我們可以做以下三件事:
    1. 返回另外一個promise
    2. 返回一個同步值
    3. 拋出一個同步錯誤

    示例:返回另外一個promise
        doSomethingFirst()
            .then(doSdoSomethingSecond)
            .then(finalHandler)
    
        //somePromise() 返回promise對象
        function doSomethingSecond(resOfDoSomethingFirst) {
            return somePromise(); //有return, finalHandler接受到的是resOfDoSomethingSecond
            //somePromise();// 沒有return, finalHandler接受到的是undefined
        }
        function finalHandler(resOfDoSomethingSecond) {
            // handle resOfDoSomethingSecond
        }

    示例:返回同步值 && 拋出一個同步錯誤
        返回同步值實際上是將同步代碼轉換爲Promisey代碼的一種很棒的方法。例如,假設我們有一個用戶的內存緩存。我們可以做到:
        
            //getUserByName  和 getUserAccountById 都返回promise對象
            getUserByName('nolan').then(function (user) {
              if (user.isLoggedOut()) { //如果用戶註銷
                throw new Error('user logged out!'); // 拋出一個同步錯誤
              }
              if (inMemoryCache[user.id]) {
                return inMemoryCache[user.id];       //  返回一個同步值!
              }
              return getUserAccountById(user.id);    // 返回一個promise!
            }).then(function (userAccount) {
              // I got a user account!
            }).catch(function (err) {
              // Boo, I got an error!
            });
            
        如果用戶註銷,catch()將收到一個同步錯誤;通過callbacks,這個錯誤會被忽略
        如果任何promise被拒絕,catch()將收到一個異步錯誤。

通常情況下,一個promise 依賴於另一個promise ,但當我們需要兩個promises的輸出。我們該怎麼做
    getUserByName('nolan').then(function (user) {
      return getUserAccountById(user.id);
    }).then(function (userAccount) {
      // 在這裏我們已經獲取到了用戶賬號userAccount,
      // 但是我們也需要user對象時該怎麼做?
    });    

    解決方案:
    function onGetUserAndUserAccount(user, userAccount) {
      return doSomething(user, userAccount);
    }
    
    function onGetUser(user) {
      return getUserAccountById(user.id).then(function (userAccount) {
        return onGetUserAndUserAccount(user, userAccount);
      });
    }
    
    getUserByName('nolan')
      .then(onGetUser)
      .then(function () {
      // at this point, doSomething() is done, and we are back to indentation 0
    });

Promise.prototype.catch(onRejected)

用於指定發生錯誤時的回調函數。catch 可以捕獲
返回值:返回一個Promise,處理狀態變爲rejected的情況
參數:
    onRejected
        是一個函數,有一個參數,用來記錄變成rejected狀態返回的原因。
        當promise 狀態變爲rejected時被調用。

注意事項:
catch(rejectHandler) 
    等同於
.then(null, rejectHandler)或.then(undefined, rejectHandler)

但是 then(resolveHandler).catch(rejectHandler) 和then(resolveHandler, rejectHandler)
不是完全相同的。
區別在於:當使用then(resolveHandler, rejectHandler)格式時,如果resolveHandler本身拋出了錯誤,那麼rejecthandler實際上不會捕獲錯誤。所以更建議使用catch 而不是then的第二個參數。

示例:
    var p1 = new Promise((resolve, reject) => {
        resolve('one');
    });
    
    // catch函數中可以捕捉到resolveHandler 中的error並打印
    p1.then(function () {
        throw new Error('oh noes');
    }).catch(function (err) {
        console.log('err=', err); // Error: oh noes
    });
    
    // reject函數不能捕捉到resolveHandler中的error
    p1.then(function () {
        throw new Error('oh noes');
    }, function (err) {
        console.log('err=', err); 
    });

Promise.prototype.finally(onFinally)

用於指定不管 Promise 對象最後狀態如何,都會執行的操作

finally本質上是then方法的特例
promise
.finally(() => {
  // 語句
});

// 等同於
promise
.then(
  result => {
    // 語句
    return result;
  },
  error => {
    // 語句
    throw error;
  }
);
返回值:返回一個Promise,這個Promise對象設置了 finally 回調函數
參數:Promise 結束後調用的函數,onFinally 這個函數不接收任何參數,它僅用於無論最終結果如何都要執行的情況。所以finally方法裏面的操作,不應依賴於 Promise 的執行結果。
示例:
    promise
    .then(result => {···})
    .catch(error => {···})
    .finally(() => {···});

Promise.resolve(value)

用於將現有對象轉爲 Promise 對象
    new Promise(function (resolve, reject) {
      resolve(someSynchronousValue);
    }).then(/* ... */);
    等價於
    Promise.resolve(someSynchronousValue).then(/* ... */);

返回值:返回一個Promise 對象,這個Promise對象是被給定的值解析過的。
參數:
    value
        將被Promise對象解析的參數
        參數類型:
            Promise對象
            具有then方法的對象
            沒有then方法的對象
            不帶有任何參數

示例1:參數是一個Promise對象
    Promise.resolve將不做任何修改、原封不動地返回這個Promise對象

    var p = Promise.resolve([1,2,3]);
    console.log("p=", p); //"p=" [object Promise]
    console.log('p type= ', typeof(p)); // "p type= " "object"
    p.then(function(v) {
      console.log("v=",v); //"v=" Array [1, 2, 3]
      console.log("v type=",typeof(v)); //"v type=" "object"
    });
    
    var p2 =  Promise.resolve(p);
    console.log("p2=", p2); //"p2=" [object Promise]
    console.log('p2 type= ', typeof(p2)); //"p2 type= " "object"
    p2.then(function(v) {
      console.log("p2 v=",v); //"p2 v=" Array [1, 2, 3]
      console.log("p2 v type=",typeof(v)); //"p2 v type=" "object"
    });

    p2 == p
示例2:參數是一個具有then方法的對象
    返回的promise會採用這個thenable的對象的最終狀態。

    let thenable = {
      then: function(resolve, reject) {
        resolve(42);
      }
    };
    
    let p = Promise.resolve(thenable);
    p.then(function(value) {
      console.log(value);  // 42
    });

    Promise.resolve方法會將thenable對象轉爲 Promise 對象,然後就立即執行thenable對象的then方法,thenable對象的then方法執行後,對象p的狀態就變爲resolved,從而立即執行最後那個then方法指定的回調函數,輸出 42

示例3:參數是一個沒有then方法的對象
    var p = Promise.resolve([1,2,3]);
    console.log("p=", p); //"p=" [object Promise]
    console.log('p type= ', typeof(p)); // "p type= " "object"
    p.then(function(v) {
      console.log("v=",v); //"v=" Array [1, 2, 3]
      console.log("v type=",typeof(v)); //"v type=" "object"
    });
    
    var p = Promise.resolve(123);
    console.log("p=", p); //"p=" [object Promise]
    console.log('p type= ', typeof(p)); // "p type= " "object"
    p.then(function(v) {
      console.log("v=",v); //"v=" 123
      console.log("v type=",typeof(v)); //"v type=" "number"
    });
    
    var p = Promise.resolve("123");
    console.log("p=", p); //"p=" [object Promise]
    console.log('p type= ', typeof(p)); // "p type= " "object"
    p.then(function(v) {
      console.log("v=",v); //"v=" "123"
      console.log("v type=",typeof(v)); //"v type=" "string"
    });

示例4:不帶有任何參數
    var p = Promise.resolve();
    console.log("p=", p); //"p=" [object Promise]
    console.log('p type= ', typeof(p)); // "p type= " "object"
    p.then(function(v) {
      console.log("v=",v); //"v=" undefined
      console.log("v type=",typeof(v)); //"v type=" "undefined"
    });

注意事項:
    立即resolve()的 Promise 對象,是在本輪“事件循環”(event loop)的結束時執行,而不是在下一輪“事件循環”的開始時
        //在下一輪“事件循環”開始時執行
        setTimeout(function () { 
          console.log('three');
        }, 0);
        //在本輪“事件循環”結束時執行
        Promise.resolve().then(function () {
          console.log('two');
        });
        //立即執行
        console.log('one');
    結果:
        "one"
        "two"
        "three"        
            

Promise.reject(reason)

返回值:返回一個Promise 對象,這個Promise 對象 帶有狀態是rejected的原因
參數:
    reason
        表示Promise被拒絕的原因
    new Promise(function (resolve, reject) {
      reject(someSynchronousReson);
    }).then(null,function(reason){
       //...
    });

等價於
    Promise.reject(someSynchronousReson)
    .then(null, function(reason) {
      //...
    });

    
示例:
    new Promise(function (resolve, reject) {
      reject("reject reason");
    }).then(null,function(reason){
            console.log(reason);//"reject reason"
    });
    
    Promise.reject("reject reason").then(null, function(reason) {
      console.log(reason); // "reject reason"
    });
    
    Promise.reject(new Error("reject reason")).then(null, function(error) {
      console.log(error); // Error: reject reason
    });

注意事項:與Promise.resolve不同的是Promise.reject()方法的參數,會原封不動地作爲reject的理由,變成後續方法的參數。
    const thenable = {
      then(resolve, reject) {
        reject('出錯了');
      }
    };
    
    Promise.reject(thenable)
    .catch(e => { //e不是reject拋出的“出錯了”這個字符串,而是thenable對象。
      console.log(e === thenable) // true
    })

Promise.all(iterable)

用於將多個 Promise 實例,包裝成一個新的 Promise 實例
返回值:返回一個新的promise對象,iterable中所有的promise都變成resolved狀態時返回的 promise纔會變爲resolved狀態; iterable中有一個promise變成rejected狀態,promise就會變爲rejected狀態。
參數:
    iterable
    一個可迭代對象,eg Array 或 String

示例: iterable中所有的promise都變成resolved狀態時返回的 promise纔會變爲resolved狀態
    var p1 = new Promise((resolve, reject) => { 
      setTimeout(resolve, 1000, 'one'); 
    }); 
    var p2 = new Promise((resolve, reject) => { 
      setTimeout(resolve, 2000, 'two'); 
    });
    var p3 = new Promise((resolve, reject) => {
      setTimeout(resolve, 3000, 'three');
    });
    const p = Promise.all([p1, p2, p3]).then(values => { 
      console.log(values); //["one", "two", "three"]
    }).catch(reason => {
      console.log(reason); //沒執行
    });

    流程:p創建時爲pending狀態,當p1, p2, p3的狀態都變成resolved時觸發p變成resolved狀態。
    p1    1s
    |----------|resolved                      
    p2    2s 
    |--------------------|resolved
    p3    3s 
    |------------------------------|resolved
                                   p resolved
                                   

示例:iterable中有一個promise變成rejected狀態,promise就會變爲rejected狀態
    var p1 = new Promise((resolve, reject) => { 
      setTimeout(resolve, 1000, 'one'); 
    }); 
    var p2 = new Promise((resolve, reject) => { 
      setTimeout(resolve, 2000, 'two'); 
    });
    var p3 = new Promise((resolve, reject) => {
      reject('reject');
    });
    const p = Promise.all([p1, p2, p3]).then(values => { 
      console.log(values); //沒執行
    }).catch(reason => {
      console.log(reason); //"reject"
    });    

    流程:p創建時爲pending狀態,p3先執行完,p3的狀態變成rejected時觸發p變成rejected狀態。
    p1    1s
    |----------|resolved                      
    p2    2s 
    |--------------------|resolved
    p3
    |-|rejected
      p rejected
      

示例:如果作爲參數的 Promise 實例,自己定義了catch方法,那麼它一旦被rejected,並不會觸發Promise.all()的catch方法
    var p1 = new Promise((resolve, reject) => { 
          setTimeout(resolve, 1000, 'one'); 
        }); 
        var p2 = new Promise((resolve, reject) => { 
          setTimeout(resolve, 2000, 'two'); 
        });
        var p3 = new Promise((resolve, reject) => {
          reject('reject');
        }).catch(reason => {
          console.log(reason); //reject
        });
        const p = Promise.all([p1, p2, p3]).then(values => { 
          console.log(values); // ["one", "two", undefined]
        }).catch(reason => {
          console.log(reason); //沒執行
        });    


    流程:p創建時爲pending狀態,p3先執行完,p3的狀態變成rejected時,調用自己定義的catch函數拋出錯誤,不會觸發p變成rejected狀態。當p1 和 p2 也執行完時,觸發p變成resolved狀態
    p1    1s
    |----------|resolved                      
    p2    2s 
    |--------------------|resolved
    p3
    |-|rejected
                           |p resolved

示例:如果傳入的參數是一個空的可迭代對象,則返回一個resolved狀態的 Promise 
    const p = Promise.all([]).then(values => { //p創建時狀態就爲resolved
      console.log(values); // []
    });

Promise.race(iterable)

同樣是將多個 Promise 實例,包裝成一個新的 Promise 實例
返回值:返回一個新的promise對象,一旦iterable中的某個promise變爲resolved或rejected狀態,返回的 promise就會變爲resolved或rejected狀態。誰最先執行完就返回誰的狀態
參數:
    iterable
    一個可迭代對象,eg Array 或 String

示例: 一旦iterable中的某個promise變爲resolved狀態,返回的 promise就會變爲resolved狀態。
    var p1 = new Promise((resolve, reject) => { 
      setTimeout(resolve, 1000, 'one'); 
    }); 
    var p2 = new Promise((resolve, reject) => { 
      setTimeout(resolve, 2000, 'two'); 
    });
    var p3 = new Promise((resolve, reject) => {
      setTimeout(resolve, 3000, 'three');
    });
    const p = Promise.race([p1, p2, p3]).then(values => { 
      console.log(values); //["one", "two", "three"]
    }).catch(reason => {
      console.log(reason); //沒執行
    });

流程:p創建時爲pending狀態,p1先執行完,p1的狀態都變成resolved時觸發p變成resolved狀態。
p1    1s
|----------|resolved
           p resolved                      
p2    2s 
|--------------------|resolved
p3    3s 
|------------------------------|resolved
                               
                                   
示例:一旦iterable中的某個promise變爲rejected狀態,返回的 promise就會變爲rejected狀態。
var p1 = new Promise((resolve, reject) => { 
  setTimeout(resolve, 1000, 'one'); 
}); 
var p2 = new Promise((resolve, reject) => { 
  setTimeout(resolve, 2000, 'two'); 
});
var p3 = new Promise((resolve, reject) => {
  reject('reject');
});
const p = Promise.all([p1, p2, p3]).then(values => { 
  console.log(values); //沒執行
}).catch(reason => {
  console.log(reason); //"reject"
});    

流程:p創建時爲pending狀態,p3先執行完,p3的狀態變成rejected時觸發p變成rejected狀態。
p1    1s
|----------|resolved                      
p2    2s 
|--------------------|resolved
p3
|-|rejected
  p rejected
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章