跳到主要內容
黯羽輕揚每天積累一點點

完全理解 Promise

免費2015-11-15#JS#Promise#Promise A+#Promise a plus#js promise#jquery promise#JavaScript Promise#Promise教程#Promise怎么用

從敬畏到疑惑到猜測到恍然大悟,「啥 - 咦 - 難道 - 哦~~」,本文詳細解釋 Promise 的內部機制

寫在前面

注意單詞對應的翻譯,有助於理解整個模式

  • promise: 承諾

  • pending: 待定

  • settled: 定了

  • resolve(d): (已)解決

  • reject(ed): (已)拒絕

  • onFulfilled: 已履行(事件)

  • onRejected: 已拒絕(事件)

  • then: 然後,接下來

  • catch: 捕獲

  • race: 賽跑

  • future: 未來(值)

  • thenable:可 then 的(有 then 屬性的)

一.Promise 是什麼

一套異步操作處理機制,Promise A+ 是規範,jQuery、ES2015、q 的 promise 都是對該規範的一種實現

二.Promise 有什麼特點

  • 更容易控制異步操作的執行順序

  • 可以在一系列異步操作中,靈活地處理錯誤

  • 支持鏈式調用,好寫

  • 能夠消除回調金字塔,好看

三.Promise 有什麼用

  • 實現串行任務隊列

  • 實現串行任務管道

  • 處理運行時才能確定執行順序的串行任務

(篇幅限制,具體適用場景以後再展開)

四.Promise 類型

內部屬性 [[PromiseStatus]]/[[PromiseValue]]

[[PromiseStatus]]:pending(待定) | resolved(已解決) | rejected(已拒絕)

後兩個統稱 settled(定了),pending 是初始狀態,如果 new Promise(func) 後,func 中的 resolvereject 都還沒有執行,此時 status=pending

[[PromiseValue]]:undefined | resolve/reject 的參數 | Promise.resolve/reject 的!thenable 參數 | onFulfilled/onRejected 的返回值

then 被調用時,該值會被傳給 thenonFulfilledonRejected,如果 resolvereject 始終沒有執行,那麼 thenonFulfilledonRejected 也始終不會執行

構造器參數 resolve/reject

resolve(解決)

執行 resolve 後,觸發 thenonFulfilled 回調,並把 resolve 的參數傳遞給 onFulfilled

reject(拒絕)

執行 reject 後,觸發 then | catchonRejected 回調,並把 reject 的參數傳遞給 onRejected

注意:如果緊跟的 then 沒有聲明 onRejected 回調,則傳給下一個 then | catchonRejected,還沒有,接著傳...如果走到頭都沒有,則報錯 Uncaught (in promise) Error:xxx

特殊的:如果 resolve 的參數是 Promise 對象,則該對象最終的 [[PromiseValue]] 會傳遞給外層 Promise 對象後續的 thenonFulfilled/onRejected,例如:

// 外層 then 能夠拿到內層 future 和 error
new Promise(function(resolve, reject) {
    resolve(new Promise(function(resolve, reject) {
        resolve(12);
        // reject(new Error('reject in nested Promise'));
    }).then(function(future) {
        return future + 1;
    }, function(error) {
        return new Error('reject again in nested Promise');
    }));
}).then(onFulfilled, onRejected);
// => 13 因為 onFulfilled 接收到了內層 promise 最終的 [[PromiseValue]]
// 如果是 reject,onFulfilled 將會接收到 Error('reject again')

五。基本語法

1.創建 Promise 對象

創建 Promise 的基本語法如下:

// 創建
var promise = new Promise(function(resolve, reject) {
    if (/*...*/) {
        resolve(data);
    }
    else {
        reject(new Error('error occurs'));
    }
});
// 使用
// then
promise.then(function(future) {
    // ...
}, function(error) {
    // ...
});
// catch
promise.catch(function(error) {
    // ...
});

注意new Promise(func) 時,func立即執行(不像 generator 只聲明不執行),執行過程中遇到 resolve() 裝沒看見(P.S.簡單理解為裝沒看見,解釋見註釋),遇到 reject() 立即拋出錯誤(注意:Chrome46 會立即拋出錯誤,FF39 不報錯,其它 polyfill,如 es6-promisepromise-polyfill 不報錯),對比例子如下:

new Promise(function(resolve, reject) {
    console.log('#1');
    resolve(1);
    console.log('#2');
});
// => #1 #2 不報錯(遇到 resolve 裝沒看見)
// 其實內部屬性 [[PromiseStatus]] 和 [[PromiseValue]] 都變了,但沒有 then 提供的 onFulfilled 回調,看不到效果

new Promise(function(resolve, reject) {
    console.log('#1');
    reject(new Error('reject'));
    console.log('#2');
});
// => #1 #2 Chrome46 報錯:Uncaught (in promise) Error: reject(…)
// 因為後面沒有 then/catch 提供的 onRejected 回調,異常沒有被消費掉

P.S.reject 的參數可以是任意類型的,但一般都傳入 Error 對象表示錯誤原因

2.Promise.prototype.then(onFulfilled, onRejected) 然後

promise 添加 onFulfilled/onRejected 回調 handler,當 promiseresolve/reject 執行時觸發對應 handler

注意:用 promise.then 添加回調函數時,如果 promise 已經處於 fulfilledrejected 狀態,那麼相應的方法將會被立即調用

返回一個 Promise 對象(鏈式調用有戲),可以簡單理解為:

  1. 如果觸發了 onFulfilled,則該對象的 [[PromiseValue]]=onFulfilled 的返回值,[[PromiseStatus]]=resolved

    new Promise(function(resolve, reject) { resolve(2); }).then(function(future) { return future * 2; }, onRejected).then(onFulfilled, onRejected); // onFulfilled 拿到了 4

  2. 如果觸發了 onRejected,則該對象的 [[PromiseValue]]=onRejected 的返回值,[[PromiseStatus]]=resolved

特別注意:返回對象的狀態是 resolved,而不是 rejected

new Promise(function(resolve, reject) {
    reject(2);
}).then(null, function(err) {
    return err * 2;
}).then(onFulfilled, onRejected);
// 還是 onFulfilled 拿到了 4,而不是 onRejected

P.S. 實際上 [[PromiseValue]]=undefined, [[PromiseStatus]]=pending,但仍將按照上述狀態來執行

3.Promise.prototype.catch(onRejected) 捕獲

等價於 then(null, onRejected),返回一個 Promise 對象,可以簡單理解為:

  1. 如果觸發了 onReject,則返回對象的 [[PromiseValue]]=onReject 的返回值,[[PromiseStatus]]=resolved

  2. 如果 onReject 始終沒有觸發,則裝作沒看見 catch,直接返回原 Promise 對象

    var p = new Promise(function(resolve, reject) { resolve(2); }).catch(function() { console.log('onRejected'); return false; }); p.then(onFulfilled, onRejected); // onFulfilled 拿到了 2,catch 沒有任何影響

P.S. 實際上 [[PromiseValue]]=undefined, [[PromiseStatus]]=pending,但仍將按照上述狀態來執行

4.Promise.all(iterable)

返回 Promise 對象,當 iterable 中所有 promiseresolve 後,觸發 onFulfilled,遇到第一個否定 promise 就立即退出,並觸發 onRejected

//1.全肯定集合,觸發 onFulfilled,傳入的參數是集合中所有 promise 的 [[PromiseValue]] 組成的數組    
Promise.all([getPromise(), getPromise()]).then(onFulfilled, onRejected);

//2.全否定集合,遇到第一個否定 promise 就退出,觸發 onRejected,傳入的參數是該 promise 的 [[PromiseValue]]
// Promise.all([getPromise(true), getPromise(true)]).then(onFulfilled, onRejected);

// 3.一般集合,同上
// Promise.all([getPromise(true), getPromise()]).then(onFulfilled, onRejected);

P.S.all,要麼全都正常完成,要麼因錯誤中斷,全都停下來

5.Promise.race(iterable) 賽跑

返回 Promise 對象,當 iterable 中有任意一個 promiseresolve 後就立即退出,並觸發 onFulfilled,遇到否定 promise 就立即退出,並觸發 onRejected

// 1.全肯定集合,遇到第一個肯定 promise 就退出,觸發 onFulfilled,傳入的參數是該 promise 的 [[PromiseValue]]
// Promise.race([getPromise(), getPromise()]).then(onFulfilled, onRejected);
// 2.全否定集合,遇到第一個否定 promise 就退出,觸發 onRejected,傳入的參數是該 promise 的 [[PromiseValue]]
// Promise.race([getPromise(true), getPromise(true)]).then(onFulfilled, onRejected);
// 3.一般集合,同上
// Promise.race([getPromise(true), getPromise()]).then(onFulfilled, onRejected);

P.S.「賽跑」,不論結果是肯定還是否定,只要最快的

6.Promise.resolve(value) 解決

  1. 如果 valuepromise,則把 promiseresolve/reject 的參數傳遞給對應的 onFulfilled/onRejected

    Promise.resolve(getPromise()).then(onFulfilled, onRejected); // onFulfilled 拿到 resolve 的參數 Promise.resolve(getPromise(true)).then(onFulfilled, onRejected); // onRejected 拿到 reject 的參數

  2. 如果 value 是 thenable 對象(有 then 屬性的對象),就會把 obj 包裝成 Promise 對象,該對象的 then 就是 obj.then

    Promise.resolve({ then: function(onFulfilled, onRejected) { // onFulfilled(1); onRejected(new Error('obj.then err'));

         console.log('obj.then');
     }
    

    }).then(onFulfilled, onRejected);

  3. 如果 value 是!thenable 值,會把該值包裝成 Promise 對象,該對象的 [[PromiseValue]]=value, [[PromiseStatus]]=resolved,所以調用 then 方法時,onFulfilled 將會接收到這個值

    Promise.resolve(1).then(onFulfilled, onRejected); // onFulfilled 拿到 1

7.Promise.reject(reason) 拒絕

會把 reason 包裝成 Promise 對象,該對象的 [[PromiseValue]]=reason, [[PromiseStatus]]=rejected,所以調用 then 方法時,onRejected 將會接收到這個值

注意:這裡沒有 thenable/!thenable 的區別,一視同仁:

// 1.!thenable onRejected 將拿到 Error 對象
Promise.reject(new Error('static reject')).then(onFulfilled, onRejected);
// 2.promise onRejected 將拿到被包起來的肯定 Promise 對象
// 該對象的 [[PromiseValue]]=promise, [[PromiseStatus]]=rejected
Promise.reject(getPromise()).then(onFulfilled, onRejected);
// 3.thenable onRejected 將拿到{then: xxx}
Promise.reject({
    then: function(onFulfilled, onRejected) {
        console.log('obj.then');
    }
}).then(onFulfilled, onRejected);

完整的測試 DEMO:promise 實例

六。高級用法

  • 配合生成器(generator

  • 配合 async/await

(篇幅限制,以後再說)

參考資料

評論

暫無評論,快來發表你的看法吧

提交評論