理解Promise规范
術語
Promise
promise 是一個擁有?then?方法的對象或函數(shù),其行為符合本規(guī)范;
thenable
是一個定義了?then?方法的對象或函數(shù),文中譯作“擁有?then?方法”;
值(value)
指任何 JavaScript 的合法值(包括?undefined?, thenable 和 promise);
異常(exception)
是使用?throw?語句拋出的一個值。
拒絕原因(reason)
表示一個 promise 的拒絕原因。
要求
? Promise 的狀態(tài) 一個 Promise 的當前狀態(tài)必須為以下三種狀態(tài)中的一種:等待態(tài)(Pending)、完成態(tài)(Fulfilled)和完成態(tài)(Rejected)。
等待態(tài)(Pending)
處于等待態(tài)時,promise 需滿足以下條件:
- 可以遷移至完成態(tài)或拒絕態(tài)
- 不能遷移至其他任何狀態(tài)
- 必須擁有一個不可變的終值
拒絕態(tài)(Rejected)
處于拒絕態(tài)時,promise 需滿足以下條件:
- 不能遷移至其他任何狀態(tài)
- 必須擁有一個不可變的據(jù)因
這里的不可變指的是恒等(即可用?===?判斷相等),而不是意味著更深層次的不可變(譯者注:?蓋指當 value 或 reason 不是基本值時,只要求其引用地址相等,但屬性值可被更改)。
Then 方法
一個 promise 必須提供一個?then?方法以訪問其當前值、終值和據(jù)因。
promise 的?then?方法接受兩個參數(shù): promise.then(onFulfilled, onRejected) 參數(shù)可選onFulfilled?和?onRejected?都是可選參數(shù)。
- 如果?onFulfilled?不是函數(shù),其必須被忽略
- 如果?onRejected?不是函數(shù),其必須被忽略
onFulfilled?特性
如果?onFulfilled?是函數(shù):
- 當?promise?執(zhí)行結束后其必須被調(diào)用,其第一個參數(shù)為?promise?的終值
- 在?promise?執(zhí)行結束前其不可被調(diào)用
- 其調(diào)用次數(shù)不可超過一次
onRejected?特性
如果?onRejected?是函數(shù):
- 當?promise?被拒絕執(zhí)行后其必須被調(diào)用,其第一個參數(shù)為?promise?的據(jù)因
- 在?promise?被拒絕執(zhí)行前其不可被調(diào)用
- 其調(diào)用次數(shù)不可超過一次
調(diào)用時機
onFulfilled?和?onRejected?只有在執(zhí)行環(huán)境堆棧僅包含平臺代碼時才可被調(diào)用?注1
調(diào)用要求
onFulfilled?和?onRejected?必須被作為函數(shù)調(diào)用(即沒有?this?值)注2 ? 注:也就是說,我們在promise中就別用this了。多次調(diào)用
then?方法可以被同一個?promise?調(diào)用多次
- 當?promise?成功執(zhí)行時,所有?onFulfilled?需按照其注冊順序依次回調(diào)
- 當?promise?被拒絕執(zhí)行時,所有的?onRejected?需按照其注冊順序依次回調(diào)
返回
then?方法必須返回一個?promise?對象?注3 promise2 = promise1.then(onFulfilled, onRejected); 注:這就是我們能夠進行鏈式調(diào)用的原因,因為then方法返回的還是一個promise對象。 如果?onFulfilled?或者?onRejected?返回一個值?x?,則運行下面的?Promise 解決過程:[[Resolve]](promise2, x)- 如果?onFulfilled?或者?onRejected?拋出一個異常?e?,則?promise2?必須拒絕執(zhí)行,并返回拒因?e
- 如果?onFulfilled?不是函數(shù)且?promise1?成功執(zhí)行,?promise2?必須成功執(zhí)行并返回相同的值
- 如果?onRejected?不是函數(shù)且?promise1?拒絕執(zhí)行,?promise2?必須拒絕執(zhí)行并返回相同的據(jù)因
Promise 解決過程
Promise 解決過程?是一個抽象的操作,其需輸入一個?promise?和一個值,我們表示為?[[Resolve]](promise, x),如果?x?有then?方法且看上去像一個 Promise ,解決程序即嘗試使?promise?接受?x?的狀態(tài);否則其用?x?的值來執(zhí)行?promise?。
這種?thenable?的特性使得 Promise 的實現(xiàn)更具有通用性:只要其暴露出一個遵循 Promise/A+ 協(xié)議的?then?方法即可;這同時也使遵循 Promise/A+ 規(guī)范的實現(xiàn)可以與那些不太規(guī)范但可用的實現(xiàn)能良好共存。
運行?[[Resolve]](promise, x)?需遵循以下步驟:
x?與?promise?相等
如果?promise?和?x?指向同一對象,以?TypeError?為據(jù)因拒絕執(zhí)行?promise
x?為 Promise
如果?x?為 Promise ,則使?promise?接受?x?的狀態(tài)?注4:
- 如果?x?處于等待態(tài),?promise?需保持為等待態(tài)直至?x?被執(zhí)行或拒絕
- 如果?x?處于完成態(tài),用相同的值執(zhí)行?promise
- 如果?x?處于拒絕態(tài),用相同的據(jù)因拒絕?promise
.then(function(data){ ? ? console.log(data); ? ? return?runAsync2(); //返回值為promise對象 })
.then(function(data){
? ? console.log(data);
? ? return?runAsync3();
})
x?為對象或函數(shù)
如果?x?為對象或者函數(shù):
- 把?x.then?賦值給?then?注5
- 如果取?x.then?的值時拋出錯誤?e?,則以?e?為據(jù)因拒絕?promise
- 如果?then?是函數(shù),將?x?作為函數(shù)的作用域?this?調(diào)用之。傳遞兩個回調(diào)函數(shù)作為參數(shù),第一個參數(shù)叫做?resolvePromise?,第二個參數(shù)叫做?rejectPromise:
-
- 如果?resolvePromise?以值?y?為參數(shù)被調(diào)用,則運行?[[Resolve]](promise, y)
- 如果?rejectPromise?以據(jù)因?r?為參數(shù)被調(diào)用,則以據(jù)因?r?拒絕?promise
- 如果?resolvePromise?和?rejectPromise?均被調(diào)用,或者被同一參數(shù)調(diào)用了多次,則優(yōu)先采用首次調(diào)用并忽略剩下的調(diào)用
- 如果調(diào)用?then?方法拋出了異常?e:
-
- 如果?resolvePromise?或?rejectPromise?已經(jīng)被調(diào)用,則忽略之
- 否則以?e?為據(jù)因拒絕?promise
- 如果?then?不是函數(shù),以?x?為參數(shù)執(zhí)行?promise
- 如果?x?不為對象或者函數(shù),以?x?為參數(shù)執(zhí)行?promise
如果一個 promise 被一個循環(huán)的?thenable?鏈中的對象解決,而?[[Resolve]](promise, thenable)?的遞歸性質(zhì)又使得其被再次調(diào)用,根據(jù)上述的算法將會陷入無限遞歸之中。算法雖不強制要求,但也鼓勵施者檢測這樣的遞歸是否存在,若檢測到存在則以一個可識別的TypeError?為據(jù)因來拒絕?promise?注6。
注釋
-
注1?這里的平臺代碼指的是引擎、環(huán)境以及 promise 的實施代碼。實踐中要確保?onFulfilled?和?onRejected?方法異步執(zhí)行,且應該在?then?方法被調(diào)用的那一輪事件循環(huán)之后的新執(zhí)行棧中執(zhí)行。這個事件隊列可以采用“宏任務(macro-task)”機制或者“微任務(micro-task)”機制來實現(xiàn)。由于 promise 的實施代碼本身就是平臺代碼(譯者注:?即都是 JavaScript),故代碼自身在處理在處理程序時可能已經(jīng)包含一個任務調(diào)度隊列或『跳板』)。
譯者注:?這里提及了 macrotask 和 microtask 兩個概念,這表示異步任務的兩種分類。在掛起任務時,JS 引擎會將所有任務按照類別分到這兩個隊列中,首先在 macrotask 的隊列(這個隊列也被叫做 task queue)中取出第一個任務,執(zhí)行完畢后取出 microtask 隊列中的所有任務順序執(zhí)行;之后再取 macrotask 任務,周而復始,直至兩個隊列的任務都取完。
兩個類別的具體分類如下:
-
- macro-task:?script(整體代碼),?setTimeout,?setInterval,?setImmediate, I/O, UI rendering
-
micro-task:?process.nextTick,?Promises(這里指瀏覽器實現(xiàn)的原生 Promise),?Object.observe,MutationObserver
詳見?stackoverflow 解答?或?這篇博客
-
注2?也就是說在?嚴格模式(strict)?中,函數(shù)?this?的值為?undefined?;在非嚴格模式中其為全局對象。
-
注3?代碼實現(xiàn)在滿足所有要求的情況下可以允許?promise2 === promise1?。每個實現(xiàn)都要文檔說明其是否允許以及在何種條件下允許?promise2 === promise1?。
-
注4?總體來說,如果?x?符合當前實現(xiàn),我們才認為它是真正的?promise?。這一規(guī)則允許那些特例實現(xiàn)接受符合已知要求的 Promises 狀態(tài)。
-
注5?這步我們先是存儲了一個指向?x.then?的引用,然后測試并調(diào)用該引用,以避免多次訪問?x.then?屬性。這種預防措施確保了該屬性的一致性,因為其值可能在檢索調(diào)用時被改變。
- 注6?實現(xiàn)不應該對?thenable?鏈的深度設限,并假定超出本限制的遞歸就是無限循環(huán)。只有真正的循環(huán)遞歸才應能導致?TypeError?異常;如果一條無限長的鏈上?thenable?均不相同,那么遞歸下去永遠是正確的行為。
總結
以上是生活随笔為你收集整理的理解Promise规范的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 朴素贝叶斯方法(Naive Bayes)
- 下一篇: promise中调用ajax