new Promise的鏈式操作的用法
複雜的概念先不講,我們先簡單粗暴地把Promise用一下,有個直觀感受。那麼第一個問題來了,Promise是什麼玩意呢?是一個類?物件?陣列?函式?
這麼一看就明白了,Promise是一個建構函式,自己身上有all、reject、resolve這幾個眼熟的方法,原型上有then、catch等同樣很眼熟的方法。這麼說用Promise new出來的物件肯定就有then、catch方法嘍,沒錯。
鏈式操作的用法
所以,從表面上看,Promise只是能夠簡化層層回撥的寫法,而實質上,Promise的精髓是“狀態”,用維護狀態、傳遞狀態的方式來使得回撥函式能夠及時呼叫,它比傳遞callback函式要簡單、靈活的多。所以使用Promise的正確場景是這樣的:
runAsync1() .then(function(data){ console.log(data); return runAsync2(); }) .then(function(data){ console.log(data); return runAsync3(); }) .then(function(data){ console.log(data); });
在then方法中,你也可以直接return資料而不是Promise物件,在後面的then中就可以接收到資料了,比如我們把上面的程式碼修改成這樣:
runAsync1() .then(function(data){ console.log(data); return runAsync2(); }) .then(function(data){ console.log(data); return '直接返回資料'; //這裡直接返回資料 }) .then(function(data){ console.log(data); });
reject的用法
function getNumber(){ return new Promise(function(resolve, reject){ //做一些非同步操作 setTimeout(function(){ var num = Math.ceil(Math.random()*10); //生成1-10的隨機數 if(num<=5){ resolve(num); } else{ reject('數字太大了'); } }, 2000); }); } getNumber() .then( function(data){ console.log('resolved'); console.log(data); }, function(reason, data){ console.log('rejected'); console.log(reason); } );
catch的用法
效果和寫在then的第二個引數裡面一樣。如果都寫上,只會進入執行then的第二個引數的方法裡。不過它還有另外一個作用:在執行resolve的回撥(也就是上面then中的第一個引數)時,如果丟擲異常了(程式碼出錯了),那麼並不會報錯卡死js,而是會進到這個catch方法中。請看下面的程式碼: getNumber() .then(function(data){ console.log('resolved'); console.log(data); console.log(somedata); //此處的somedata未定義 }) .catch(function(reason){ console.log('rejected'); console.log(reason); });
也就是說進到catch方法裡面去了,而且把錯誤原因傳到了reason引數中。即便是有錯誤的程式碼也不會報錯了,這與我們的try/catch語句有相同的功能。
all的用法
Promise .all([runAsync1(), runAsync2(), runAsync3()]) .then(function(results){ console.log(results); }); 用Promise.all來執行,all接收一個數組引數,裡面的值最終都算返回Promise物件。這樣,三個非同步操作的並行執行的,等到它們都執行完後才會進到then裡面。那麼,三個非同步操作返回的資料哪裡去了呢?
都在then裡面呢,all會把所有非同步操作的結果放進一個數組中傳給then,就是上面的results。所以上面程式碼的輸出結果就是:
race的用法
all方法的效果實際上是「誰跑的慢,以誰為準執行回撥」,那麼相對的就有另一個方法「誰跑的快,以誰為準執行回撥」,這就是race方法,這個詞本來就是賽跑的意思。race的用法與all一樣,
//請求某個圖片資源
function requestImg(){
var p = new Promise(function(resolve, reject){
var img = new Image();
img.onload = function(){
resolve(img);
}
img.src = 'xxxxxx';
});
return p;
}
//延時函式,用於給請求計時
function timeout(){
var p = new Promise(function(resolve, reject){
setTimeout(function(){
reject('圖片請求超時');
}, 5000);
});
return p;
}
Promise
.race([requestImg(), timeout()])
.then(function(results){
console.log(results);
})
.catch(function(reason){
console.log(reason);
});
requestImg函式會非同步請求一張圖片,我把地址寫為”xxxxxx”,所以肯定是無法成功請求到的。timeout函式是一個延時5秒的非同步操作。我們把這兩個返回Promise物件的函式放進race,於是他倆就會賽跑,如果5秒之內圖片請求成功了,那麼遍進入then方法,執行正常的流程。如果5秒鐘圖片還未成功返回,那麼timeout就跑贏了,則進入catch,報出“圖片請求超時”的資訊。執行結果如下:
舉個例子
function isexist(arr){ return new Promise(resolve,reject=>{ if(isNaN(arr)){ resolve('值是NAN') }else{ reject('值是'+arr) } }) } isexist(n).then(res=>{console.log(res)}).catch(err=>console.log(err)) 值是2 Promise {<fulfilled>: undefined} isexist(NaN).then(res=>{console.log(res)}).catch(err=>console.log(err)) 值是NAN Promise {<fulfilled>: undefined}
Promise物件可以實現鏈式呼叫,舉個例子
function isexist(arr) {
return new Promise((resolve, reject) => {
if (!isNaN(arr)) {
resolve(arr)
} else {
reject(arr)
}
})
}
function initNum(arr) {
return new Promise((resolve, reject) => {
if (!isNaN(arr)) {
arr = 123456
resolve('值是' + arr)
} else {
arr = 'promise'
reject('值是' + arr)
}
})
}
//採用鏈式呼叫,最常見的場景就是確認彈出框
isexist(555)
.then(res=>initNum(res))
.then(res => { console.log(res,123) })
.catch(res=>initNum(res))
.catch(err => console.log(err,456))
// 值是123456 123