摘要:首先從這個(gè)構(gòu)造函數(shù)說(shuō)起,它是全局對(duì)象的屬性的值,這也就是為什么瀏覽器環(huán)境下我們能直接調(diào)用它的原因,就像這些構(gòu)造函數(shù)一樣。的產(chǎn)生就是像正常使用構(gòu)造函數(shù)那樣構(gòu)建一個(gè),不過(guò)傳給構(gòu)造函數(shù)是內(nèi)部自動(dòng)創(chuàng)建的,作用是把記錄到中。
> new Promise((resolve, reject) => setTimeout(resolve, 1000, "foo")) > .then(console.log) > // foo (1s后)
在使用 Promise 的時(shí)候,我們最簡(jiǎn)單的理解與用法就是像上面的代碼那樣,把異步結(jié)果提供給 resolve 作參數(shù),然后通過(guò)給 then 方法傳遞一個(gè)自定義函數(shù)作為結(jié)果處理函數(shù)。但 resolve 和 reject 這兩個(gè)參數(shù)到底是什么?在這背后,它的基本工作方式到底是怎樣的呢?讓我們從規(guī)范的角度來(lái)初步了解它吧。
參考: ES8 Promise
TL;DRpromise 的工作機(jī)制與 callback 類似,都采用內(nèi)部的抽象操作 Job 來(lái)實(shí)現(xiàn)異步
Promise 構(gòu)造函數(shù)里的 resolve/reject 函數(shù)是內(nèi)部創(chuàng)建的,在調(diào)用它們時(shí)傳入的參數(shù)就是要解析的結(jié)果,把它和 promise 已經(jīng)存儲(chǔ)的用戶傳入的處理函數(shù)一起插入到 Job 隊(duì)列中。傳入的參數(shù)也可以是一個(gè) promise,在 Promise.all/race 的內(nèi)部就有用到。
Promise.prototype.then 根據(jù)當(dāng)前的 promise 的狀態(tài)來(lái)決定是立即將 promise 中存儲(chǔ)的結(jié)果取出并和參數(shù)中的處理函數(shù)一起直接插入到 Job 隊(duì)列中還是先與 promise 關(guān)聯(lián)起來(lái)作為結(jié)果處理函數(shù)。then 會(huì)隱式調(diào)用 Promise 構(gòu)建函數(shù)構(gòu)建新的 promise 并返回。
Promise.all 先創(chuàng)建一個(gè)新的 promise,然后先、初始化一個(gè)空的結(jié)果數(shù)組和一個(gè)計(jì)數(shù)器來(lái)對(duì)已經(jīng) resolve 的 promise進(jìn)行計(jì)數(shù),之后會(huì)進(jìn)行迭代,對(duì)于每個(gè)迭代值它都會(huì)為其創(chuàng)造一個(gè)promise,并設(shè)定這個(gè)promise的then為向結(jié)果數(shù)組里添加結(jié)果以及計(jì)數(shù)器--,當(dāng)計(jì)數(shù)器減至0時(shí)就會(huì)resolve最終結(jié)果。
Promise.race 也是會(huì)創(chuàng)建一個(gè)新的主 promise,之后主要是根據(jù) promise 只能 resolve 一次的限制,對(duì)于每個(gè)迭代值都會(huì)創(chuàng)造另一個(gè)promise,先resolve的也就會(huì)先被主 promise resolve 返回結(jié)果。
new Promise(executor)首先從 Promise 這個(gè)構(gòu)造函數(shù)說(shuō)起,它是全局對(duì)象的 Promise 屬性的值,這也就是為什么瀏覽器環(huán)境下我們能直接調(diào)用它的原因,就像 String, Array 這些構(gòu)造函數(shù)一樣。
new Promise(executor)的第一步就像其他構(gòu)造函數(shù)一樣,按照 Promise 的 prototype 來(lái)構(gòu)建一個(gè)新對(duì)象,并初始化了幾個(gè)內(nèi)部插槽[[PromiseState]],[[PromiseResult]],[[PromiseFullfillReactions]],[[PromiseRejectReactions]],[[PromiseIsHandled]]來(lái)記錄一些相關(guān)的信息,可以從名字來(lái)大致推斷出他們的作用,詳情我們下文再提。這里它們的初始值除了[[PromiseResult]]依次為 "pending",空 list,空 list,false。
下一步,ES 會(huì)根據(jù)這個(gè) promise 對(duì)象來(lái)生成用來(lái)resolve promise的 resolve function 和用來(lái) reject promise 的 reject function。然后調(diào)用 executor,以 resolve function 和 reject function 為參數(shù),如果在這個(gè)過(guò)程中出錯(cuò)了,就直接 reject promise。最后返回 promise。
那什么又是 resolve,什么又是 reject 呢。我們知道 Promise 的狀態(tài),也就是[[PromiseState]]有三種值: pending, fullfilled, rejected,用 reject function 就可以 reject promise,把它的狀態(tài)從 pending 變?yōu)閞ejected。不過(guò) resolve function 既可以 fullfill promise 來(lái)把promise的狀態(tài)從 pending 變?yōu)?fullfilled,也可以用來(lái) reject promise。
那么 resolve function 和 reject function 到底做了些什么呢?
先來(lái)看 reject function ,首先在生成它的時(shí)候,會(huì)給它初始化[[Promise]]和[[AlreadyResolved]]插槽,也就是把它和某個(gè) promise 關(guān)聯(lián)起來(lái)。在執(zhí)行時(shí),會(huì)傳入一個(gè)參數(shù) reason,并只有當(dāng)[[AlreadyResolved]]是 false,也就是還沒(méi) resolve 過(guò)、狀態(tài)為 pending 時(shí),才會(huì)調(diào)用返回 RejectPromise、傳入 promise 和 reason 參數(shù)來(lái) reject promise,否則返回 undefined。
RejectPromise(promise, reason),除了把[[PromiseState]]從 pending 變?yōu)?rejected 之外,還會(huì)把 promise 的結(jié)果[[PromiseResult]]的值設(shè)為 reason,并會(huì)取出 promise 的[[PromiseRejectReactions]]中已存的記錄(相信讀者們已經(jīng)明白后面還會(huì)有一個(gè)操作來(lái)向這個(gè)內(nèi)部插槽里存記錄),并用 TriggerPromiseReactions 調(diào)用這些記錄做后續(xù)處理,并傳入 reject 的原因 reason。類似的,resolve function 中用到的 FullfillPromise(promise, value) 操作把 promise 的狀態(tài)變?yōu)?fulfilled,抽取[[PromiseFullfillReactions]]的值調(diào)用 TriggerPromiseReactions,并傳入 fulfilled 的結(jié)果 value。
TriggerPromiseReactions(reactions, argument) 會(huì)調(diào)用 EnqueueJob("PromiseJobs", PromiseReactionJob, <
再來(lái)看 resolve function,與 reject function 一樣,在生成它時(shí),會(huì)把它與某個(gè) promise 關(guān)聯(lián)起來(lái)。在執(zhí)行時(shí),我們傳入的參數(shù)叫做 resolution。如果 promise 已經(jīng) resolve 過(guò),就返回 undefined。之后的情況就相對(duì)復(fù)雜一些了。
如果用戶把這個(gè) promise 本身傳給了 resolve function 作為參數(shù) resolution,就會(huì)創(chuàng)建一個(gè) TypeError,throw 它,并調(diào)用 RejectPromise,reason 參數(shù)為這個(gè) TypeError。
如果 resolution 的類型不是 Object,就調(diào)用 FulfillPromise(promise, resolution)。
其余的情況就是 resolution 是除了自身以外的帶 then 的對(duì)象 (Promise) 的情況了。
如果 resolution 是個(gè)不帶then的對(duì)象,就 RejectPromise。
如果有 then 屬性但不能調(diào)用,也 FulfillPromise, 。
如果有 then 屬性并且可以調(diào)用,就 EnqueueJob("PromiseJobs", PromiseResolveThenableJob, <
在說(shuō)明 EnqueueJob 之前,先來(lái)看看 Job 是個(gè)什么東西。簡(jiǎn)單來(lái)說(shuō),它就像是回調(diào)的內(nèi)部實(shí)現(xiàn)機(jī)制:“當(dāng)沒(méi)有其他 ES 在跑時(shí),初始化并執(zhí)行自己對(duì)應(yīng)的 ES。“。我們有一個(gè)待執(zhí)行的 FIFO 的 Job 隊(duì)列,以及當(dāng)前的執(zhí)行環(huán)境 running execution context 和 execution context stack,當(dāng)后兩者均為空時(shí),才會(huì)執(zhí)行 Job 隊(duì)列的第一個(gè)。
ES 規(guī)定實(shí)現(xiàn)里至少要有兩個(gè) Job 隊(duì)列,ScriptJobs 和 PromiseJobs。當(dāng)我們調(diào)用 EnqueueJob("PromiseJobs", ...)時(shí),也就將要完成的 Job 和它們的參數(shù)插入到了 PromiseJobs 這個(gè)隊(duì)列??梢钥吹?,Promise 下有兩種 Job
PromiseReactionJob(reaction, argument)
reaction 有三個(gè)內(nèi)部插槽 [[Capability]]、[[Type]] 和 [[Handler]],分別表示 [[關(guān)聯(lián)的 promise 及相關(guān)的resolve function 和 reject function]]、[[類別]]、[[handler]]。如果用戶沒(méi)有給 handler(undefined),就根據(jù)類別是 Fulfill 還是 Reject 來(lái)把 argument 當(dāng)作結(jié)果。如果給了 handler,就用它來(lái)對(duì) argument 進(jìn)行進(jìn)一步處理。最后根據(jù)這個(gè)結(jié)果來(lái)用 resolve function 和 reject function 進(jìn)行處理并返回。
PromiseResolveThenableJob(promiseToResolve, thenable, then)
創(chuàng)建和 promiseToResolve 關(guān)聯(lián)的 resolve function 和 reject function。以 then 為調(diào)用函數(shù),thenable 為this,resolve function和reject function 為參數(shù)調(diào)用返回。
首先是創(chuàng)建一個(gè) promiseCapability,它包含了一個(gè)新的 promise 和相關(guān)聯(lián)的 resolve function 和 reject function。promise 的產(chǎn)生就是像正常使用 Promise 構(gòu)造函數(shù)那樣構(gòu)建一個(gè) promise,不過(guò)傳給構(gòu)造函數(shù) executor 是內(nèi)部自動(dòng)創(chuàng)建的,作用是把 resolve/reject function 記錄到PromiseCapability中。
根據(jù) promiseCapability 和 onfulfilled/onrejected 創(chuàng)建兩個(gè)分別用于 fulfill 和 reject 的PromiseReaction,也就是 PromiseJobs 里最終要執(zhí)行的操作。
如果當(dāng)前的 promise(this)是 pending 狀態(tài),就把這兩個(gè) reaction 分別插入到 promise的[[PromiseFulfillReactions]]和[[PromiseRejectReactions]]隊(duì)列中。但如果此時(shí) promise 已經(jīng)是 fulfilled 或是 rejected 狀態(tài)了,就從 promise 的[[PromiseResult]]取出值 result,作為 fulfilled 的結(jié)果/reject 的原因,插入到 Job 隊(duì)列里,EnqueueJob("PromiseJobs", PromiseReactionJob, <
像 then 那樣創(chuàng)建一個(gè) promiseCapability,然后直接調(diào)用其中的 resolve function 并傳入要解析的值x,最后返回其中的新 promise.
Promise.all(iterable)Promise.all也會(huì)像 then 那樣創(chuàng)建一個(gè) promiseCapability,里面包含著一個(gè)新的 promise 及其關(guān)聯(lián)的 resolve function 和 reject function,之后就結(jié)合迭代器循環(huán):
如果迭代完了并且計(jì)數(shù)器為0則調(diào)用 promiseCapability 的 resolve function 來(lái) resolve 結(jié)果數(shù)組
否則計(jì)數(shù)器加1,然后取出下一個(gè)迭代的值,傳給 Promise.resolve 也構(gòu)建一個(gè)新的 promise,然后內(nèi)部創(chuàng)建一個(gè) Promise.all Resolve Element Function,傳給這個(gè)新 promise 的 then 用來(lái)把結(jié)果添加到結(jié)果數(shù)組并使計(jì)數(shù)器減一。
Promise.race(iterable)同樣的,創(chuàng)建一個(gè) promiseCapability,然后進(jìn)行迭代,用 Promise.resolve 來(lái)構(gòu)建一個(gè)新的 promise,之后調(diào)用這個(gè)新 promise 的 then 方法,傳入 promiseCapability 里的 resolve/reject function,結(jié)合之前提到的 promise 只會(huì) resolve 一次,可以看到確實(shí)很有 race 的意味。
結(jié)語(yǔ)看到這里,不知道大家是否對(duì) Promise 有了更深的理解了呢。再往深一步,ES6里新提出的 async/await 實(shí)際上也是應(yīng)用了 Generator 的思想與 Promise,感興趣的話可以繼續(xù)了解一下。
文 / Kacxxia并沒(méi)有作者介紹
本文已由作者授權(quán)發(fā)布,版權(quán)屬于創(chuàng)宇前端。歡迎注明出處轉(zhuǎn)載本文。本文鏈接:https://knownsec-fed.com/2018-08-22-shen-ru-promise/
想要看到更多來(lái)自知道創(chuàng)宇開(kāi)發(fā)一線的分享,請(qǐng)搜索關(guān)注我們的微信公眾號(hào):創(chuàng)宇前端(KnownsecFED)。
歡迎點(diǎn)贊、收藏、留言評(píng)論、轉(zhuǎn)發(fā)分享和打賞支持我們。打賞將被完全轉(zhuǎn)交給文章作者。
感謝您的閱讀。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/97773.html
摘要:的翻譯文檔由的維護(hù)很多人說(shuō),阮老師已經(jīng)有一本關(guān)于的書了入門,覺(jué)得看看這本書就足夠了。前端的異步解決方案之和異步編程模式在前端開(kāi)發(fā)過(guò)程中,顯得越來(lái)越重要。為了讓編程更美好,我們就需要引入來(lái)降低異步編程的復(fù)雜性。 JavaScript Promise 迷你書(中文版) 超詳細(xì)介紹promise的gitbook,看完再不會(huì)promise...... 本書的目的是以目前還在制定中的ECMASc...
摘要:有一個(gè)和相關(guān)的更大的問(wèn)題。最后,請(qǐng)負(fù)有責(zé)任感并且使用安全的擴(kuò)展。深入理解五部曲異步問(wèn)題深入理解五部曲轉(zhuǎn)換問(wèn)題深入理解五部曲可靠性問(wèn)題深入理解五部曲擴(kuò)展性問(wèn)題深入理解五部曲樂(lè)高問(wèn)題最后,安利下我的個(gè)人博客,歡迎訪問(wèn) 原文地址:http://blog.getify.com/promis... 現(xiàn)在,我希望你已經(jīng)看過(guò)深入理解Promise的前三篇文章了。并且假設(shè)你已經(jīng)完全理解Promises...
摘要:回調(diào)函數(shù)模式類似于事件模型,因?yàn)楫惒酱a也會(huì)在后面的一個(gè)時(shí)間點(diǎn)才執(zhí)行如果回調(diào)過(guò)多,會(huì)陷入回調(diào)地獄基礎(chǔ)可以當(dāng)做是一個(gè)占位符,表示異步操作的執(zhí)行結(jié)果。函數(shù)可以返回一個(gè),而不必訂閱一個(gè)事件或者向函數(shù)傳遞一個(gè)回調(diào)函數(shù)。 主要知識(shí)點(diǎn):Promise生命周期、Promise基本操作、Promise鏈、響應(yīng)多個(gè)Promise以及集成PromiseshowImg(https://segmentfaul...
摘要:等待的基本語(yǔ)法該關(guān)鍵字的的意思就是讓編譯器等待并返回結(jié)果。這里并不會(huì)占用資源,因?yàn)橐婵梢酝瑫r(shí)執(zhí)行其他任務(wù)其他腳本或處理事件。接下來(lái),我們寫一個(gè)火箭發(fā)射場(chǎng)景的小例子不是真的發(fā)射火箭 本文由云+社區(qū)發(fā)表 本篇文章,小編將和大家一起學(xué)習(xí)異步編程的未來(lái)——async/await,它會(huì)打破你對(duì)上篇文章Promise的認(rèn)知,竟然異步代碼還能這么寫! 但是別太得意,你需要深入理解Promise后,...
摘要:從最開(kāi)始的到封裝后的都在試圖解決異步編程過(guò)程中的問(wèn)題。為了讓編程更美好,我們就需要引入來(lái)降低異步編程的復(fù)雜性。寫一個(gè)符合規(guī)范并可配合使用的寫一個(gè)符合規(guī)范并可配合使用的理解的工作原理采用回調(diào)函數(shù)來(lái)處理異步編程。 JavaScript怎么使用循環(huán)代替(異步)遞歸 問(wèn)題描述 在開(kāi)發(fā)過(guò)程中,遇到一個(gè)需求:在系統(tǒng)初始化時(shí)通過(guò)http獲取一個(gè)第三方服務(wù)器端的列表,第三方服務(wù)器提供了一個(gè)接口,可通過(guò)...
摘要:前言中的異步,剛開(kāi)始的時(shí)候都是用回調(diào)函數(shù)實(shí)現(xiàn)的,所以如果異步嵌套的話,就有出現(xiàn)回調(diào)地獄,使得代碼難以閱讀和難以維護(hù),后來(lái)出現(xiàn)了,解決了回調(diào)地獄的問(wèn)題。 前言 js中的異步,剛開(kāi)始的時(shí)候都是用回調(diào)函數(shù)實(shí)現(xiàn)的,所以如果異步嵌套的話,就有出現(xiàn)回調(diào)地獄,使得代碼難以閱讀和難以維護(hù),后來(lái)es6出現(xiàn)了promise,解決了回調(diào)地獄的問(wèn)題?,F(xiàn)在我們就自己寫代碼實(shí)現(xiàn)一下promise,這樣才能深入理解...
閱讀 2067·2021-11-24 10:45
閱讀 1917·2021-10-09 09:43
閱讀 1361·2021-09-22 15:38
閱讀 1310·2021-08-18 10:19
閱讀 2887·2019-08-30 15:55
閱讀 3115·2019-08-30 12:45
閱讀 3044·2019-08-30 11:25
閱讀 429·2019-08-29 11:30