1. 程式人生 > >十分鐘帶你解讀什麼是Promise非同步程式設計

十分鐘帶你解讀什麼是Promise非同步程式設計

Promise的含義

Promise是非同步程式設計的一種解決方案,比傳統的解決方案--回撥函式和事件--更合理且更強大。

Promise物件具有以下兩個特點:

  1. 物件的狀態不受外界影響。Promise物件代表一個非同步操作,有3中狀態:Pending(進行中)、Fulfilled(已成功)和Rejected(已失敗)。
  2. 一旦狀態改變就不會再變,任何時候都可以得到這個結果。

Promise也有一些缺點:

  1. 無法取消Promise,一旦新建它就會立即執行,無法中途取消。
  2. 如果不設定回撥函式,Promise內部丟擲的錯誤不會反應奧外部。
  3. 當處於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物件的狀態從“未完成”變為“成功”。reject函式的作用是,將Promise物件的狀態從“未完成”變為“失敗”。

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決定,分為兩種情況:

  1. 只有p1、 p2、 p3的狀態都變成Fulfilled,p的狀態才會變成Fulfilled,此時p1、 p2、 p3的返回值組成一個數組,傳遞給p的回撥函式。
  2. 只要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)   //非同步載入失敗則呼叫該方法

);