摘要:如果實(shí)現(xiàn)滿足所有要求,則實(shí)現(xiàn)可能允許。本條款允許使用特定于實(shí)現(xiàn)的方法來采用已知一致承諾的狀態(tài)。接下來根據(jù)規(guī)范進(jìn)行手寫實(shí)現(xiàn)注釋偷懶就將對(duì)應(yīng)的規(guī)范標(biāo)注出來,其實(shí)基本上就是對(duì)著規(guī)范實(shí)現(xiàn)。
如果要手寫實(shí)現(xiàn)promise,那么先看看promise/A+規(guī)范,再來實(shí)現(xiàn),將會(huì)事半功倍。
那么我先翻譯一下Promise/A+規(guī)范中的內(nèi)容。
術(shù)語
1.1 promise 是一個(gè)帶有符合此規(guī)范的then方法的對(duì)象或者函數(shù)。
1.2 thenable 是一個(gè)定義了一個(gè)then方法的對(duì)象或者函數(shù)。
1.3 value 是一個(gè)任意合法的JavaScript值(包括undefined, thenable,或者promise)。
1.4 exception 是一個(gè)使用throw語句拋出的值。
1.5 reason 是一個(gè)指出為什么promise被rejected的值。
要求
2.1 promise 狀態(tài)
一個(gè)promise必須是三種狀態(tài)其中的一種狀態(tài):pending,fulfilled 或者 rejected。
2.1.1 當(dāng)promise處于pending狀態(tài)時(shí):
2.1.1.1 可以轉(zhuǎn)變到 fulfilled 或者 rejected 狀態(tài)。
2.1.2 當(dāng)promise處于fulfilled狀態(tài)時(shí):
2.1.2.1 一定不能夠轉(zhuǎn)變到其他任何一種狀態(tài)。 2.1.2.2 必須有一個(gè)value,并且這個(gè)值一定不能改變。
2.1.3 當(dāng)promise處于rejected狀態(tài)時(shí):
2.1.3.1 一定不能夠轉(zhuǎn)變到其他任何一種狀態(tài)。 2.1.3.2 必須有一個(gè)reason,并且這個(gè)值不能夠改變。
在這里,“一定不能改變”意味著不可變的身份(即===),但并不意味著深層不變性。(個(gè)人理解為是value/reason指向的地址是不可變的,但假若value/reason為一個(gè)對(duì)象,則對(duì)象內(nèi)的值是可變的。)
2.2 then 方法
一個(gè)promise必須提供一個(gè)then方法去訪問當(dāng)前或者最終的value或者reason。
一個(gè)promise的then方法接受兩個(gè)參數(shù):
promise.then(onFulfilled, onRejected)
2.2.1 onFulfilled 和 onRejected 都是可選的參數(shù):
2.2.1.1 如果 onFulfilled 不是一個(gè)函數(shù),它必須被忽略。 2.2.1.2 如果 onRejected 不是一個(gè)函數(shù),它必須被忽略。
2.2.2 如果 onFulfilled 是一個(gè)函數(shù):
2.2.2.1 promise 是 fulfilled 狀態(tài)時(shí)它必須被調(diào)用,并且 promise 的 value 作為它的第一個(gè)參數(shù)。 2.2.2.2 它一定不能在 promise 進(jìn)入 fulfilled 狀態(tài)之前被調(diào)用。 2.2.2.3 它一定不能夠調(diào)用超過一次。
2.2.3 如果 onRejected 時(shí)一個(gè)函數(shù):
2.2.3.1 promise 是 rejected 狀態(tài)時(shí)它必須被調(diào)用,并且 promise 的 reason 作為它的第一個(gè)參數(shù)。 2.2.3.2 它一定不能在 promise 進(jìn)入 rejected 狀態(tài)之前被調(diào)用。 2.2.3.3 它一定不能夠調(diào)用超過一次。
2.2.4 直到執(zhí)行上下文堆棧僅包含平臺(tái)代碼之前,onFulfilled 或 onRejected 不能夠被調(diào)用。[3.1]
2.2.5 onFulfilled 和 onRejected 必須以函數(shù)的形式被調(diào)用(即沒有this值)。[3.2]
2.2.6 then 可以在同一個(gè)promise被調(diào)用多次:
2.2.6.1 當(dāng) promise 處于 fulfilled 狀態(tài)時(shí),各個(gè) onFulfilled 回調(diào)必須按其原始調(diào)用的順序執(zhí)行。 2.2.6.2 當(dāng) promise 處于 rejected 狀態(tài)時(shí),各個(gè) onRejected 回調(diào)必須按其原始調(diào)用的順序執(zhí)行。
2.2.7 then 必須返回一個(gè)promise [3.3]:
promise2 = promise1.then(onFulfilled, onRejected);
2.2.7.1 如果 onFulfilled 或 onRejected 返回一個(gè)值 x,運(yùn)行Promise解決程序 [[Resolve]](promise2, x)。 2.2.7.2 如果 onFulfilled 或 onRejected 拋出一個(gè)意外 e,promise2 必須以 e 為 reason 被 rejected。 2.2.7.3 如果 onFulfilled 不是一個(gè)函數(shù)并且 promise1 處于 fulfilled 狀態(tài),promise2 必須以與 promise1 同樣的 value 轉(zhuǎn)變到 fulfilled 狀態(tài)。 2.2.7.4 如果 onRejected 不是一個(gè)函數(shù)并且 promise1 處于 rejected狀態(tài),promise2 必須以與 promise1 同樣的 reason 轉(zhuǎn)變到 rejected狀態(tài)。
2.3 Promise解決程序
promise解決程序是一個(gè)抽象的操作,它把一個(gè) promise 和一個(gè) value 作為輸入,我們將這個(gè)表示為 [[Resolve]](promise, x)。如果 x 是一個(gè) thenable ,它將會(huì)試圖讓 promise 采用 x 的狀態(tài),前提是x的行為至少有點(diǎn)像一個(gè) promise。否則,它將會(huì)用值 x 執(zhí)行 promise。
對(duì)這些 thenable 的處理使得與 promise 實(shí)現(xiàn)方式能夠去互相操作。只要它們公開了符合 Promise/A+ 的 then 方法。它還使得 promises/A+ 實(shí)現(xiàn)方式能夠采用合理的 then 方法去“同化”不一致的實(shí)現(xiàn)方式。
為了運(yùn)行[[Resolve]](promise, x),執(zhí)行以下步驟:
2.3.1 如果 promise 與 x 是同一個(gè)對(duì)象,以 Tyeperror 作為 reason 去 reject promise。
2.3.2 如果 x 是一個(gè) promise,使用它的狀態(tài):
2.3.2.1 如果 x 處于 pending 狀態(tài),promise 必須保持 pending 狀態(tài)直到 x 處于 fulfilled 或者 rejected 狀態(tài)。 2.3.2.2 如果 x 處于 fulfilled 狀態(tài),以相同的 value 去 fulfill promise。 2.3.2.3 如果 x 處于 rejected 狀態(tài),以相同的 reason 去 reject promise。
2.3.3 否則,如果 x 是一個(gè)對(duì)象或者函數(shù):
2.3.3.1 讓 then 作為 x.then。 2.3.3.2 如果取屬性 x.then 會(huì)導(dǎo)致拋出異常 e,則以 e 為 reason reject promise。 2.3.3.3 如果 then 是一個(gè)函數(shù),讓 x 作為 this 調(diào)用它,第一個(gè)參數(shù)為 resolvePromise,第二個(gè)參數(shù)為 rejectPromise,然后: 2.3.3.3.1 如果使用value y 調(diào)用 resolvepromise 時(shí),運(yùn)行[[Resolve]](promise, y)。 2.3.3.3.2 如果使用reason r 調(diào)用 rejectPromise 時(shí),也用 r reject promise。 2.3.3.3.3 如果 resolvePromise 和 rejectPromise 都被調(diào)用了,或多次調(diào)用同一參數(shù),那么第一個(gè)調(diào)用優(yōu)先,其他的調(diào)用都會(huì)被忽略。 2.3.3.3.4 如果調(diào)用 then 的過程中拋出了一個(gè)意外 e, 2.3.3.3.4.1 如果 resolvePromise 或者 rejectPromise 被調(diào)用了,那么忽略它。 2.3.3.3.4.2 否則,把 e 作為 reason reject promise。 2.3.3.4 如果 then 不是一個(gè)函數(shù),將 x 作為參數(shù)執(zhí)行 promise。
2.3.4 如果 x 不是一個(gè)對(duì)象或者函數(shù),將 x 作為參數(shù)執(zhí)行 promise。
如果一個(gè)參與了 thenable 循環(huán)鏈的 thenable 去 resolve promise,這樣 [[Resolve]](promise, thenable) 的遞歸性質(zhì)最終會(huì)導(dǎo)致 [[Resolve]](promise, thenable) 會(huì)被再次調(diào)用,遵循上述算法將會(huì)導(dǎo)致無限遞歸。我們鼓勵(lì)去實(shí)現(xiàn)(但不是必需的)檢測(cè)這樣的遞歸,并以 TypeError 作為 reason 去 reject Promise。[3.6]
注意
3.1 這里的“平臺(tái)代碼”指的是引擎,環(huán)境和 promise 實(shí)現(xiàn)代碼。實(shí)際上,這個(gè)要求保證了 onFulfilled 和 onRejected 將會(huì)異步執(zhí)行,在事件循環(huán)之后,用一個(gè)新的堆棧來調(diào)用它。 這可以通過“宏任務(wù)”機(jī)制(如 settimeou t或 setimmediate )或“微任務(wù)”機(jī)制(如 mutationobserver 或 process.nextick)來實(shí)現(xiàn)。由于 Promise 實(shí)現(xiàn)被視為平臺(tái)代碼,因此它本身可能包含一個(gè)任務(wù)調(diào)度隊(duì)列或“trampoline”,并在其中調(diào)用處理程序。
3.2 也就是說,在 strict 模式下,這(指的是this)在它們內(nèi)部將會(huì)是 undefined;在 sloppy 模式下,它將會(huì)是全局對(duì)象。
3.3 如果實(shí)現(xiàn)滿足所有要求,則實(shí)現(xiàn)可能允許 promise2 == promise1。每個(gè)實(shí)現(xiàn)都應(yīng)該記錄它是否能夠生成 promise2 == promise1 以及在什么條件下。
3.4 一般來說,只有當(dāng) X 來自當(dāng)前的實(shí)現(xiàn)時(shí),才知道它是一個(gè)真正的 promise。本條款允許使用特定于實(shí)現(xiàn)的方法來采用已知一致承諾的狀態(tài)。
3.5 此過程首先存儲(chǔ)對(duì) x 的引用,然后測(cè)試該引用,然后調(diào)用該引用,避免多次訪問 x.then 屬性。這些預(yù)防措施對(duì)于確保訪問器屬性的一致性非常重要,訪問器屬性的值可能在兩次檢索之間發(fā)生更改。
3.6 實(shí)現(xiàn)方式中不應(yīng)當(dāng)在 thenbale 鏈中的深度設(shè)置主觀的限制,并且不應(yīng)當(dāng)假設(shè)鏈的深度超過主觀的限制后會(huì)是無限的。只有真正的循環(huán)才能導(dǎo)致 TypeError。如果遇到由無限多個(gè)不同 thenable 組成的鏈,那么永遠(yuǎn)遞歸是正確的行為。
接下來根據(jù)規(guī)范進(jìn)行手寫實(shí)現(xiàn),注釋偷懶就將對(duì)應(yīng)的規(guī)范標(biāo)注出來,其實(shí)基本上就是對(duì)著規(guī)范實(shí)現(xiàn)。
function promise(fuc){ //接收一個(gè)函數(shù)作為參數(shù) let that = this; that.value = null; // 2.1.2.2 that.reason = null;// 2.1.3.2 that.onFulfilled = []; // 2.2.6 that.onRejected = []; //2.2.6 that.status = "pending"; // 2.1 function resolve(val){ if (that.status === "pending") { // 2.1.2 that.status = "fulfilled"; that.value = val; that.onFulfilled.forEach(fc => fc(val)); //2.2.6.1 } } function reject(err){ if (that.status === "pending") { //2.1.3 that.status = "rejected"; that.reason = err; that.onRejected.forEach(fc => fc(err)); //2.2.6.2 } } try { fuc(resolve,reject); } catch (error) { reject(error); } } promise.prototype.then = function (onFulfilled, onRejected) //2.2 { let that = this, promise2; //2.2.7 onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (x) => x; //2.2.1 2.2.7.3 onRejected = typeof onRejected === "function" ? onRejected : (e) => { throw e }; // 2.2.7.4 switch (that.status) { case "pending": promise2 = new promise((reslove, reject)=>{ that.onFulfilled.push(()=>{ setTimeout(() => { try { let x = onFulfilled(that.value); //2.2.2.1 ResolutionProcedure(promise2, x, reslove, reject); //2.2.7.1 } catch (err) { reject(err); //2.2.7.2 } }, 0); }); that.onRejected.push(()=>{ setTimeout(() => { try { let x = onRejected(that.reason); //2.2.3.1 ResolutionProcedure(promise2, x, reslove, reject); //2.2.7.1 } catch (err) { reject(err); //2.2.7.2 } }, 0); }); }) break; case "fulfilled": promise2 = new promise((reslove,reject)=>{ setTimeout(() => { // 2.2.4 try{ let x = onFulfilled(that.value); //2.2.2.1 ResolutionProcedure(promise2, x, reslove, reject); //2.2.7.1 }catch(err){ reject(err); //2.2.7.2 } }, 0); }) break; case "rejected": promise2 = new promise((reslove, reject)=>{ setTimeout(() => { // 2.2.4 try{ let x = onRejected(that.reason); //2.2.3.1 ResolutionProcedure(promise2, x, reslove, reject); //2.2.7.1 }catch(err){ reject(err); //2.2.7.2 } }, 0); }) break; default: break; } return promise2; } function ResolutionProcedure(promise, x, reslove, reject){ //2.3 if (promise === x) //2.3.1 return reject(new TypeError("same promise")); if (x instanceof Promise) //2.3.2 x.then(((xvalue)=>{ ResolutionProcedure(promise, xvalue, reslove, reject); },(xreason)=>{ reject(xreason) })); if (x !== null &&( typeof x === "object" || typeof x === "function" )) //2.3.3 { try { let then = x.then;//2.3.3.1 if(typeof then === "function") //2.3.3.3 { let isuse = false; try { then.call(x,(y)=>{ if (!isuse) { ResolutionProcedure(promise, y, reslove, reject); //2.3.3.3.1 isuse = true; } },(r)=>{ if (!isuse) { reject(r) //2.3.3.3.2 isuse = true; } }) } catch (error) { if(!isuse) reject(error) //2.3.3.3.4.2 } }else{ reslove(x); //2.3.3.4 } } catch (error) { reject(error); //2.3.3.2 } }else{ reslove(x); //2.3.4 } }
做完之后可以通過 promise test 進(jìn)行測(cè)試。
先在代碼下面加上一些測(cè)試需要代碼。
promise.deferred = function () { let def = {}; def.promise = new promise(function (resolve, reject) { def.resolve = resolve; def.reject = reject; }); return def; } module.exports = promise
然后跑一下下面的代碼即可。
npm install -g promises-aplus-tests promises-aplus-tests promise.js
結(jié)果全部通過,說明符合規(guī)范:
參考:鏈接實(shí)現(xiàn)一個(gè)完美符合Promise/A+規(guī)范的Promise
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/102741.html
摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。寫一個(gè)符合規(guī)范并可配合使用的寫一個(gè)符合規(guī)范并可配合使用的理解的工作原理采用回調(diào)函數(shù)來處理異步編程。 JavaScript怎么使用循環(huán)代替(異步)遞歸 問題描述 在開發(fā)過程中,遇到一個(gè)需求:在系統(tǒng)初始化時(shí)通過http獲取一個(gè)第三方服務(wù)器端的列表,第三方服務(wù)器提供了一個(gè)接口,可通過...
摘要:的翻譯文檔由的維護(hù)很多人說,阮老師已經(jīng)有一本關(guān)于的書了入門,覺得看看這本書就足夠了。前端的異步解決方案之和異步編程模式在前端開發(fā)過程中,顯得越來越重要。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。 JavaScript Promise 迷你書(中文版) 超詳細(xì)介紹promise的gitbook,看完再不會(huì)promise...... 本書的目的是以目前還在制定中的ECMASc...
摘要:手寫一款符合規(guī)范的長(zhǎng)篇預(yù)警有點(diǎn)長(zhǎng),可以選擇性觀看。初始狀態(tài)是,狀態(tài)可以有或者不能從轉(zhuǎn)換為或者從轉(zhuǎn)換成即只要由狀態(tài)轉(zhuǎn)換為其他狀態(tài)后,狀態(tài)就不可變更。 手寫一款符合Promise/A+規(guī)范的Promise 長(zhǎng)篇預(yù)警!有點(diǎn)長(zhǎng),可以選擇性觀看。如果對(duì)Promise源碼不是很清楚,還是推薦從頭看,相信你認(rèn)真從頭看到尾,并且去實(shí)際操作了,肯定會(huì)有收獲的。主要是代碼部分有點(diǎn)多,不過好多都是重復(fù)的,不...
摘要:如果狀態(tài)是等待態(tài)的話,就往回調(diào)函數(shù)中函數(shù),比如如下代碼就會(huì)進(jìn)入等待態(tài)的邏輯以上就是簡(jiǎn)單版實(shí)現(xiàn)實(shí)現(xiàn)一個(gè)符合規(guī)范的接下來大部分代碼都是根據(jù)規(guī)范去實(shí)現(xiàn)的。 為更好的理解, 推薦閱讀Promise/A+ 規(guī)范 實(shí)現(xiàn)一個(gè)簡(jiǎn)易版 Promise 在完成符合 Promise/A+ 規(guī)范的代碼之前,我們可以先來實(shí)現(xiàn)一個(gè)簡(jiǎn)易版 Promise,因?yàn)樵诿嬖囍校绻隳軐?shí)現(xiàn)出一個(gè)簡(jiǎn)易版的 Promise ...
摘要:本文同時(shí)也發(fā)布在我的博客上,歡迎之前也手寫過簡(jiǎn)單的,這次則是為了通過官方的測(cè)試集,借鑒了一些下載量較多的,改了幾遍,終于是通過了規(guī)范的個(gè)測(cè)試用例如何測(cè)試測(cè)試庫(kù)地址在這,大家在寫完自己的后,不妨也去測(cè)試一下,檢驗(yàn)自己的是否符合規(guī)范。 本文同時(shí)也發(fā)布在我的github博客上,歡迎star~ 之前也手寫過簡(jiǎn)單的promise,這次則是為了通過官方的Promise A+測(cè)試集,借鑒了一些下載量...
閱讀 3278·2023-04-26 00:57
閱讀 600·2021-10-08 10:05
閱讀 1345·2021-09-08 09:36
閱讀 4147·2021-08-12 13:31
閱讀 2542·2019-08-30 15:55
閱讀 2237·2019-08-30 15:55
閱讀 1013·2019-08-30 15:55
閱讀 2684·2019-08-29 13:17