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