Promise使用詳解(轉)
2015年6月, ES2015(即 ECMAScript 6、ES6) 正式釋出。其中 Promise 被列為正式規範,成為 ES6 中最重要的特性之一。
1.then()方法
簡單來講,then 方法就是把原來的回撥寫法分離出來,在非同步操作執行完後,用鏈式呼叫的方式執行回撥函式。
而 Promise 的優勢就在於這個鏈式呼叫。我們可以在 then 方法中繼續寫 Promise 物件並返回,然後繼續呼叫 then 來進行回撥操作。
(1)下面通過樣例作為演示,我們定義做飯、吃飯、洗碗(cook、eat、wash)這三個方法,它們是層層依賴的關係,下一步的的操作需要使用上一部操作的結果。(這裡使用 setTimeout 模擬非同步操作)
//做飯 function cook(){ console.log('開始做飯。'); var p = new Promise(function(resolve, reject){ //做一些非同步操作 setTimeout(function(){ console.log('做飯完畢!'); resolve('雞蛋炒飯'); }, 1000); }); return p; } //吃飯 function eat(data){ console.log('開始吃飯:' + data); var p = new Promise(function(resolve, reject){ //做一些非同步操作 setTimeout(function(){ console.log('吃飯完畢!'); resolve('一塊碗和一雙筷子'); }, 2000); }); return p; } function wash(data){ console.log('開始洗碗:' + data); var p = new Promise(function(resolve, reject){ //做一些非同步操作 setTimeout(function(){ console.log('洗碗完畢!'); resolve('乾淨的碗筷'); }, 2000); }); return p; }
(2)使用 then 鏈式呼叫這三個方法:
cook()
.then(function(data){
return eat(data);
})
.then(function(data){
return wash(data);
})
.then(function(data){
console.log(data);
});
當然上面程式碼還可以簡化成如下:
cook()
.then(eat)
.then(wash)
.then(function(data){
console.log(data);
});
(3)執行結果如下:
2.reject()方法
上面樣例我們通過 resolve 方法把 Promise 的狀態置為完成態(Resolved),這時 then 方法就能捕捉到變化,並執行“成功”情況的回撥。
而 reject 方法就是把 Promise 的狀態置為已失敗(Rejected),這時 then 方法執行“失敗”情況的回撥(then 方法的第二引數)。
(1)下面同樣使用一個樣例做演示
//做飯
function cook(){
console.log('開始做飯。');
var p = new Promise(function(resolve, reject){ //做一些非同步操作
setTimeout(function(){
console.log('做飯失敗!');
reject('燒焦的米飯');
}, 1000);
});
return p;
}
//吃飯
function eat(data){
console.log('開始吃飯:' + data);
var p = new Promise(function(resolve, reject){ //做一些非同步操作
setTimeout(function(){
console.log('吃飯完畢!');
resolve('一塊碗和一雙筷子');
}, 2000);
});
return p;
}
cook()
.then(eat, function(data){
console.log(data + '沒法吃!');
})
執行結果如下:
(2)如果我們只要處理失敗的情況可以使用 then(null, …),或是使用接下來要講的 catch 方法。
cook()
.then(null, function(data){
console.log(data + '沒法吃!');
})
3.catch()方法
(1)它可以和 then 的第二個引數一樣,用來指定 reject 的回撥
cook()
.then(eat)
.catch(function(data){
console.log(data + '沒法吃!');
});
(2)它的另一個作用是,當執行 resolve 的回撥(也就是上面 then 中的第一個引數)時,如果丟擲異常了(程式碼出錯了),那麼也不會報錯卡死 js,而是會進到這個 catch 方法中。
//做飯
function cook(){
console.log('開始做飯。');
var p = new Promise(function(resolve, reject){ //做一些非同步操作
setTimeout(function(){
console.log('做飯完畢!');
resolve('雞蛋炒飯');
}, 1000);
});
return p;
}
//吃飯
function eat(data){
console.log('開始吃飯:' + data);
var p = new Promise(function(resolve, reject){ //做一些非同步操作
setTimeout(function(){
console.log('吃飯完畢!');
resolve('一塊碗和一雙筷子');
}, 2000);
});
return p;
}
cook()
.then(function(data){
throw new Error('米飯被打翻了!');
eat(data);
})
.catch(function(data){
console.log(data);
});
執行結果如下:
這種錯誤的捕獲是非常有用的,因為它能夠幫助我們在開發中識別程式碼錯誤。比如,在一個 then() 方法內部的任意地方,我們做了一個 JSON.parse() 操作,如果 JSON 引數不合法那麼它就會丟擲一個同步錯誤。用回撥的話該錯誤就會被吞噬掉,但是用 promises 我們可以輕鬆的在 catch() 方法裡處理掉該錯誤。
(3)還可以新增多個 catch,實現更加精準的異常捕獲。
somePromise.then(function() {
return a();
}).catch(TypeError, function(e) {
//If a is defined, will end up here because
//it is a type error to reference property of undefined
}).catch(ReferenceError, function(e) {
//Will end up here if a wasn't defined at all
}).catch(function(e) {
//Generic catch-the rest, error wasn't TypeError nor
//ReferenceErro;
});
4,all()方法
Promise 的 all 方法提供了並行執行非同步操作的能力,並且在所有非同步操作執行完後才執行回撥。
(1)比如下面程式碼,兩個個非同步操作是並行執行的,等到它們都執行完後才會進到 then 裡面。同時 all 會把所有非同步操作的結果放進一個數組中傳給 then。
//切菜
function cutUp(){
console.log('開始切菜。');
var p = new Promise(function(resolve, reject){ //做一些非同步操作
setTimeout(function(){
console.log('切菜完畢!');
resolve('切好的菜');
}, 1000);
});
return p;
}
//燒水
function boil(){
console.log('開始燒水。');
var p = new Promise(function(resolve, reject){ //做一些非同步操作
setTimeout(function(){
console.log('燒水完畢!');
resolve('燒好的水');
}, 1000);
});
return p;
}
Promise
.all([cutUp(), boil()])
.then(function(results){
console.log("準備工作完畢:");
console.log(results);
});
(2)執行結果如下:
5,race()方法
race 按字面解釋,就是賽跑的意思。race 的用法與 all 一樣,只不過 all 是等所有非同步操作都執行完畢後才執行 then 回撥。而 race 的話只要有一個非同步操作執行完畢,就立刻執行 then 回撥。
注意:其它沒有執行完畢的非同步操作仍然會繼續執行,而不是停止。
(1)這裡我們將上面樣例的 all 改成 race
Promise
.race([cutUp(), boil()])
.then(function(results){
console.log("準備工作完畢:");
console.log(results);
});
(2)race 使用場景很多。比如我們可以用 race 給某個非同步請求設定超時時間,並且在超時後執行相應的操作。
//請求某個圖片資源
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 函式非同步請求一張圖片,timeout 函式是一個延時 5 秒的非同步操作。我們將它們一起放在 race 中賽跑。
- 如果 5 秒內圖片請求成功那麼便進入 then 方法,執行正常的流程。
- 如果 5 秒鐘圖片還未成功返回,那麼則進入
catch,報“圖片請求超時”的資訊。