Promise
Promise 是異步編程的一種解決方案,比傳統的解決方案——回調函數和事件——更合理且更強大。它最早由社區提出並實現,ES6將其寫進了語言標準,統一了用法,並原生提供了Promise對象。
特點
- 對象的狀態不受外界影響 (3種狀態)
- Pending狀態(進行中)
- Fulfilled狀態(已成功)
- Rejected狀態(已失敗)
- 一旦狀態改變就不會再變 (兩種狀態改變:成功或失敗)
- Pending -> Fulfilled
- Pending -> Rejected
用法
創建Promise實例
var promise = new Promise(function(resolve, reject){
// ... some code
if (/* 異步操作成功 */) {
resolve(value);
} else {
reject(error);
}
})
Promise構造函數接受一個函數作爲參數,該函數的兩個參數分別是resolve
和reject
。它們是兩個函數,由JavaScript引擎提供,不用自己部署。
resolve作用是將Promise對象狀態由“未完成”變爲“成功”,也就是Pending -> Fulfilled
,在異步操作成功時調用,並將異步操作的結果作爲參數傳遞出去;而reject函數則是將Promise對象狀態由“未完成”變爲“失敗”,也就是Pending -> Rejected
,在異步操作失敗時調用,並將異步操作的結果作爲參數傳遞出去。
then
Promise實例生成後,可用then
方法分別指定兩種狀態回調參數。then 方法可以接受兩個回調函數作爲參數:
- Promise對象狀態改爲Resolved時調用 (必選)
- Promise對象狀態改爲Rejected時調用 (可選)
基本用法示例
function sleep(ms) {
return new Promise(function(resolve, reject) {
setTimeout(resolve, ms);
})
}
sleep(500).then( ()=> console.log("finished"));
這段代碼定義了一個函數sleep,調用後,等待了指定參數(500)毫秒後執行then中的函數。值得注意的是,Promise新建後就會立即執行。
執行順序
接下來我們探究一下它的執行順序,看以下代碼:
let promise = new Promise(function(resolve, reject){
console.log("AAA");
resolve()
});
promise.then(() => console.log("BBB"));
console.log("CCC")
// AAA
// CCC
// BBB
執行後,我們發現輸出順序總是 AAA -> CCC -> BBB
。表明,在Promise新建後會立即執行,所以首先輸出 AAA
。然後,then方法指定的回調函數將在當前腳本所有同步任務執行完後纔會執行,所以BBB 最後輸出
。
與定時器混用
首先看一個實例:
let promise = new Promise(function(resolve, reject){
console.log("1");
resolve();
});
setTimeout(()=>console.log("2"), 0);
promise.then(() => console.log("3"));
console.log("4");
// 1
// 4
// 3
// 2
可以看到,結果輸出順序總是:1 -> 4 -> 3 -> 2
。1與4的順序不必再說,而2與3先輸出Promise的then,而後輸出定時器任務。原因則是Promise屬於JavaScript引擎內部任務,而setTimeout則是瀏覽器API,而引擎內部任務優先級高於瀏覽器API任務,所以有此結果。
拓展 async/await
async
顧名思義,異步。async函數對 Generator 函數的改進,async 函數必定返回 Promise,我們把所有返回 Promise 的函數都可以認爲是異步函數。特點體現在以下四點:
- 內置執行器
- 更好的語義
- 更廣的適用性
- 返回值是 Promise
await
顧名思義,等待。正常情況下,await命令後面是一個 Promise 對象,返回該對象的結果。如果不是 Promise 對象,就直接返回對應的值。另一種情況是,await命令後面是一個thenable對象(即定義then方法的對象),那麼await會將其等同於 Promise 對象。
混合使用
先看示例:
function sleep(ms) {
return new Promise(function(resolve, reject) {
setTimeout(resolve,ms);
})
}
async function handle(){
console.log("AAA")
await sleep(5000)
console.log("BBB")
}
handle();
// AAA
// BBB (5000ms後)
我們定義函數sleep,返回一個Promise。然後在handle函數前加上async關鍵詞,這樣就定義了一個async函數。在該函數中,利用await來等待一個Promise。
Promise優缺點
優點 | 缺點 |
---|---|
解決回調 | 無法監測進行狀態 |
鏈式調用 | 新建立即執行且無法取消 |
減少嵌套 | 內部錯誤無法拋出 |