ES6 Promise

ES6 Promise

1. 基礎

Promise對象用於表示一個異步操做的最終狀態(完成或失敗),以及該異步操做的結果值。javascript

用人話來說,所謂Promise,就是一個用來傳遞異步操做消息的對象。html

《深刻理解ES6》中這樣描述Promsie:java

Promise至關於異步操做結果的佔位符,它不會去訂閱一個事件,也不會傳遞一個回調函數給目標函數,而是讓函數返回一個Promise,承諾將在將來的某個時刻完成。node

語法:git

new Promise(function(resolve, reject) { } /*executor*/ )

2. 生命週期

graph TD A[unsettled 未處理 - pending 進行中] -->|異步操做| B[settled 已處理] B --> C[Fulfilled 成功] B --> D[Rejected 失敗]

內部屬性[[PromiseState]]被用來表示Promise的三種狀態:pending、fulfilled、rejected。此屬性不可訪問,而是經過當Promise的狀態改變時,使用then()方法來執行後續程序。全部Promise都有then()方法,它接受兩個參數:es6

  • fulfillment function 完成函數:當Promise的狀態變爲fulfilled時調用
  • rejection function 拒絕函數:當Promise的狀態變爲rejected時調用

Promise的catch()方法等價於then()方法的第二個參數。github

若是一個Promise處於已處理狀態,在這以後添加到任務隊列中的處理程序仍將執行:數組

let promise = readFile('example.txt');
promise.then(function(contents) {
    console.log(contents);
    
    promise.then(function(contents) {
        console.log(contents);
    });
});

每次調用then()方法或catch()方法都會建立一個新任務,當Promise被解決(resolved)時執行。promise

3. 建立未完成的Promise

用Promise構造函數能夠建立新的Promise。 構造函數值接受一個參數:executor(resolve, reject)執行器函數。 執行器成功完成時調用resolve()方法,失敗時調用reject()瀏覽器

Promise的執行器會當即執行,而後才執行後續流程中的代碼:

let promise = new Promise(function(resolve, reject) {
    console.log('Promise');
    resolve();
});
console.log('Hi');
// 結果
// Promise
// Hi

調用resolve()後會觸發一個異步操做,傳入then()catch()方法的函數會被添加到任務中並異步執行。

let promise = new Promise(function(resolve, reject) {
    console.log('Promise');
    resolve();
});
promise.then(function() {
    console.log('Resolved');
});
console.log('Hi');

// 結果
// Promise
// Hi
// Resolved

4. 建立已處理的Promise

Promise.resolve() 接受一個參數並返回一個完成態的Promise。 Promise.reject() 建立一個已拒絕的Promise。

let promise = Promise.resolve(42);
promise.then(function(value) {
    console.log(value);            // 42
});

若是向Promise.resolve()Promise.reject()傳入一個Promise,那麼這個Promise會被直接返回。

二者還能夠接受非Promise的Thenable對象做爲參數。好比:

// 已完成
let thenable = {
    then: function(resolve, reject) {
        resolve(42);
    }
};
let promise = Promise.resolve(thenable);
promise.then(function(value) {
    console.log(value);            // 42
});

// 已拒絕
let thenable1 = {
    then: function(resolve, reject) {       
        reject(24);    
    }};
let promise1 = Promise.resolve(thenable1);
promise1.catch(function(value) {   
    console.log(value); // 24
});

5. 執行器錯誤

執行器內部錯誤也會被Promise的拒絕處理程序捕獲。

6. 全局的Promise拒絕處理

因爲Promise在沒有提供拒絕處理程序時,不會提示失敗信息或拋出錯誤。這就須要一些方法來捕獲、處理全局的Promise。 *可是個人Chrome(版本 73.0.3683.103(正式版本) (64 位))有未catch的promise會在控制檯拋出錯誤 。以下圖所示:

*

6.1 Node.js環境

process對象提供如下兩個事件處理Promise拒絕。

  • unhandledRejection 在一個事件循環中,當Promise被拒絕,而且沒有提供拒絕處理程序時觸發
  • rejectionHandled 在一個事件循環後,當Promise被拒絕,且拒絕處理程序被調用時觸發

詳細參見《深刻理解ES6》P248。

6.2 瀏覽器環境

與Node.js環境不一樣的就是這兩個事件在window對象上:

  • unhandledrejection 在一個事件循環中,當Promise被拒絕,而且沒有提供拒絕處理程序時,觸發該事件
  • rejectionhandled 在一個事件循環後,當Promise被拒絕時,若拒絕處理程序被調用,觸發該事件

兩個事件處理程序接受一個有如下屬性的事件對象做爲參數:

  • type 事件名稱(unhandledrejection或rejectionhandled)
  • promise 被拒絕的Promise對象
  • reason 來自Promise的拒絕值
let rejected;

window.onunhandledrejection = function(event) {
    console.log(event.type);                           // unhandledrejection
    console.log(event.reason.message);            // Explosion
    console.log(rejected === event.promise);   // true
};

// FIXME 實測這個事件並無被執行
// 因爲當前瀏覽器端這兩個事件兼容性很是差
// 暫時不作細緻探究
window.onrejectionhandled = function(event) {
    console.log(event.type);                           // rejectionhandled
    console.log(event.reason.message);            // Explosion
    console.log(rejected === event.promise);   // true
};

rejected = Promise.reject(new Error('Explosion'));

7. 串聯Promise

每次調用then()方法或catch()方法時實際上建立並返回了另外一個Promise,只有當第一個Promise完成或被拒絕後,第二個纔會被解決。

let p1 = new Promise(function(resolve, reject) {
    resolve(42);
});

p1.then(function(value) {
    console.log(value);
}).then(function() {
    console.log('Finished');
});

// 結果
// 42
// Finished

7.1 捕獲錯誤

務必在Promise鏈的末尾留有一個拒絕處理程序以確保可以正確處理所可能發生的錯誤。

let p2 = new Promise(function (resolve, reject) {
    throw new Error('Explosion');
});

p2.catch(function (reason) {
    console.log(reason.message);
    throw new Error('Boom');
}).catch(function (reason) {
    console.log(reason.message);
});

// 結果
// Explosion
// Boom

7.2 Promise鏈的返回值

Promise鏈的另外一個重要特性是能夠給下游Promise傳遞數據;若是在完成處理程序中指定一個返回值,則能夠沿着這條鏈繼續傳遞數據。

let p3 = new Promise(function (resolve, reject) {
    resolve(42);
});
p3.then(function (value) {
    console.log(value);
    return value + 1;
}).then(function (value) {
    console.log(value);
});

// 結果
// 42
// 43

拒絕處理程序也能夠傳遞值:

let p4 = Promise.reject(42);

p4.catch(function (reason) {    
    console.log(reason);    
    return reason + 1;
}).then(function (value) {    
    console.log(value);
});

// 結果
// 42
// 43

7.3 Promise鏈中返回Promise

let p5 = Promise.resolve(42);
let p6 = Promise.resolve(43);

p5.then(function (value) {    
    // 第一個完成處理程序    
    console.log(value);    // 42    
    return p6;
}).then(function (value) {    
    // 第二個完成處理程序    
    console.log(value);    // 43
});

上面是最多見的用法。在完成處理程序中建立新的Promise能夠推遲完成處理程序的執行。想要在一個Promise被解決後當即觸發另外一個Promise,這個模式頗有用。

let p7 = Promise.resolve(42);

p7.then(function (value) {    
    console.log(value);   // 42        
    return Promise.resolve(43);
}).then(function (value) {    
    console.log(value);   // 43
});

8. 響應多個Promise

Promise.all()Promise.race()兩個方法能夠同時監聽多個Promise。

8.1 Promise.all()方法

Promise.all()方法只接受一個參數並返回一個Promise。 該參數是一個含有多個受監視Promise的可迭代對象,只有當可迭代對象中全部的Promise都被解決後返回的Promsie纔會被解決,只有當可迭代對象中全部Promise都被完成後返回的Promise纔會被完成。

let p11 = new Promise(function (resolve, reject) {    
    resolve(42);
});
let p12 = new Promise(function (resolve, reject) {    
    resolve(43);
});
let p13 = new Promise(function (resolve, reject) {   
    resolve(44);
});

let p14 = Promise.all([p11, p12, p13]);
p14.then(function (value) {    
    console.log(Array.isArray(value));   // true    
    console.log(value);                     // (3) [42, 43, 44]
});

全部傳入Promise.all()的Promise只要有一個被拒絕,那麼返回的Promsie再也不等全部Promise都完成就當即執行拒絕程序。 拒絕處理程序老是接受一個值而不是數組。這個值來自第一個被拒絕Promise的拒絕值。

8.2 Promise.race()方法

不一樣於all()方法,只要有一個Promise被解決,返回的Promise就被解決無須等到全部Promise都被完成。

實際上,傳給Promise.race()方法的Promsie會進行競選,以決出哪個先被解決,若是先解決的是已完成Promise,則返回已完成Promise;若是先解決的是已拒絕Promsie,則返回已拒絕Promise。

let t21 = 1000,   
     t22 = 2000;
let p21 = new Promise(function (resolve, reject) {    
    setTimeout(function () {        
        resolve(42);    
    }, t21);
});
let p22 = new Promise(function (resolve, reject) {    
    setTimeout(function () {        
        reject(43);    
    }, t22);
});

let p23 = Promise.race([p21, p22]);
p23.then(function (value) {    
    console.log(value);
}).catch(function (reason) {    
    console.log(reason);
});

// 結果
// 42

以上例子因爲p21先完成,因此執行了then方法。若是咱們把t21改爲3000,那麼最終會執行catch方法,輸出43

9. 自Promise繼承

Promise與其餘內建類型同樣,也能夠做爲基類派生其餘類,能夠定義本身的Promise變量來擴展內建Promise功能。

參見《深刻理解ES6》P262。

10. 基於Promise的異步任務執行

參見《深刻理解ES6》P263。 這裏再也不贅述。

Demo 源碼 The end... Last updated by: Jehorn, April 17, 2019, 5:38 PM

相關文章
相關標籤/搜索