關於ES6的Promise

掘金5025發表於2018-04-09

JavaScript的非同步處理

提到JavaScript的非同步處理,也許很多人和我一樣想到利用回撥函式。

例如:

firstAsync(function(data){
    //處理得到的 data 資料
    //....
    secondAsync(function(data2){
        //處理得到的 data2 資料
        //....
        thirdAsync(function(data3){
              //處理得到的 data3 資料
              //....
        });
    });
});
複製程式碼

但是回撥函式一旦巢狀層級過多,就會引發“回撥地獄”的問題,而Promise作為一種非同步解決方案,就可以很好的解決這個問題。

例如上面的例子用Promise實現的話:

firstAsync()
.then(function(data){
    //處理得到的 data 資料
    //....
    return secondAsync();
})
.then(function(data2){
    //處理得到的 data2 資料
    //....
    return thirdAsync();
})
.then(function(data3){
    //處理得到的 data3 資料
    //....
});
複製程式碼

什麼是 Promise?

Promise是一個物件,用於表示一個非同步操作的最終狀態(完成或失敗),以及其返回的值。它作為ES6最重要的特性之一,目前已經被Firefox和Chrome等主流瀏覽器所支援,具體情況可以在Can I use裡檢視。如果想要做瀏覽器的相容,只需要在瀏覽器中載入Polyfill類庫即可。

Promise 的狀態

  • 等待(pending):初始狀態。
  • 已完成(fulfilled):意味著操作成功完成。
  • 已失敗(rejected):意味著操作失敗。

Promise物件的狀態只可能處於這三種之一,它的狀態改變只可能是兩種可能:從 Pending 變為 fulfilled 和從 Pending 變為 Rejected。一旦狀態發生改變,就不會再變,這也是Promise[承諾]這個名字的由來。

then 鏈式操作和 resolve 方法

Promise.prototype.then 方法返回的是一個新的 Promise 物件,因此可以採用鏈式寫法。該方法接收兩個引數,第一個是成功的回撥,第二個是失敗的回撥。

下面用setTimeout模擬非同步操作:

function runAsync1(){
    var p = new Promise(function(resolve, reject){
        setTimeout(function(){
            console.log('非同步操作1執行完畢!');
            resolve('非同步操作1成功回撥資料');
        }, 1000);
    });
    return p;
}
function runAsync2(){
    var p = new Promise(function(resolve, reject){
        setTimeout(function(){
            console.log('非同步操作2執行完畢!');
            resolve('非同步操作2成功回撥資料');
        }, 1000);
    });
    return p;
}
複製程式碼

使用 Promise.prototype.then 方法鏈式呼叫這兩個方法:

runAsync1()
.then(function(data){
    console.log(data);
    return runAsync2();
})
.then(function(data){
   console.log(data);
});
複製程式碼

執行結果如下:

關於ES6的Promise

你也可以在 then 方法中直接return資料而不是Promise物件:

runAsync1()
.then(function(data){
    console.log(data);
    return runAsync2();
})
.then(function(data){
   console.log(data);
   return '直接返回資料'
})
.then(function(data){
    console.log(data)
});
複製程式碼

執行結果如下:

關於ES6的Promise

reject 方法

上面我們通過 Promise.resolve() 把Promise的狀態置為已完成(Resolved), 然後通過 then 方法捕捉到變化,並執行“成功”情況的回撥,而 Promise.reject() 的作用就是將Promise的狀態置為已失敗(rejected),同樣通過 then 方法來執行“失敗”情況的回撥。

function runAsync1(num){
    var p = new Promise(function(resolve, reject){  
        setTimeout(function(){
            console.log('非同步操作1執行完畢!');
            if(num < 10) {
                resolve('非同步操作1成功回撥資料');
            }else{
                reject('非同步操作1失敗回撥資料'); 
            }

        }, 1000);
    });
    return p;
}
複製程式碼

執行結果如下:

關於ES6的Promise

catch 方法

Promise.prototype.catch 和 Promise.prototype.then 方法的第二個引數一樣,用來指定reject的回撥,二者都返回 promise 物件, 因此都可以被鏈式呼叫。

關於ES6的Promise

此外 catch 還可以捕獲異常:

關於ES6的Promise

all 方法

Promise.all(iterable) 返回一個新的promise物件,該promise物件只有在iterable引數物件裡所有的promise物件都成功的時候才會觸發成功,並所有promise返回值以陣列的形式返回。

以上面的 runAsync1() 和 runAsync1() 為例:

Promise
.all([runAsync1(),runAsync2()])
.then(function(results){
    console.log(results);
});
複製程式碼

返回結果如下:

關於ES6的Promise

race 方法

Promise.race() 方法,race即競賽,顧名思義,這是一個比誰跑得快的規則。

function runAsync1(){
    var p = new Promise(function(resolve, reject){
        setTimeout(function(){
            console.log('非同步操作1執行完畢!');
            resolve('非同步操作1成功回撥資料');
        }, 2000);
    });
    return p;
}
function runAsync2(){
    var p = new Promise(function(resolve, reject){
        setTimeout(function(){
            console.log('非同步操作2執行完畢!');
            resolve('非同步操作2成功回撥資料');
        }, 1000);
    });
    return p;
}
Promise
.race([runAsync1(),runAsync2()])
.then(function(results){
    console.log(results);
});
複製程式碼

執行結果如下:

關於ES6的Promise

可以看到,只要runAsync1、runAsync2中的任何一個率先改變狀態,父Promise的狀態就跟著改變。而那個率先改變狀態的子Promise的返回值,就是傳遞給父Promise的返回值。

總結

Promise的基本使用方法就是這樣,之前看過一篇文章,用做飯(cook)吃飯(eat)洗碗(wash)為例,形象地演示了Promise的用法,地址戳戳戳這裡

相關文章