<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          JavaScript 可視化:Promise執(zhí)行徹底搞懂

          共 5244字,需瀏覽 11分鐘

           ·

          2024-04-26 09:17

               

          大廠技術(shù)  高級(jí)前端  Node進(jìn)階

          點(diǎn)擊上方 程序員成長(zhǎng)指北,關(guān)注公眾號(hào)

          回復(fù)1,加入高級(jí)Node交流群

          深入探討了 JavaScript 中 Promise 的內(nèi)部機(jī)制,解釋了它們?nèi)绾问巩惒饺蝿?wù)以非阻塞方式執(zhí)行,并展示了 Promise 的創(chuàng)建、狀態(tài)變化以及與事件循環(huán)的關(guān)系。

          正文從這開(kāi)始~~

          JavaScript 中的 Promise 一開(kāi)始可能會(huì)讓人感到有些難以理解,但是如果我們能夠理解其內(nèi)部的工作原理,就會(huì)發(fā)現(xiàn)它們其實(shí)是非常易于掌握的。

          在這篇博客文章中,我們將深入探討 Promise 的一些內(nèi)部機(jī)制,并探索它們是如何使得 JavaScript 能夠執(zhí)行非阻塞的異步任務(wù)。

          一種創(chuàng)建 Promise 的方式是使用 new Promise 構(gòu)造函數(shù),它接收一個(gè)執(zhí)行函數(shù),該函數(shù)帶有 resolve 和 reject 參數(shù)。

           new Promise((resolve, reject) => {
          // TODO(Lydia): Some async stuff here
          });

          當(dāng) Promise 構(gòu)造函數(shù)被調(diào)用時(shí),會(huì)發(fā)生以下幾件事情:

          • 創(chuàng)建一個(gè) Promise 對(duì)象。這個(gè) Promise 對(duì)象包含幾個(gè)內(nèi)部槽,包括 [[PromiseState]][[PromiseResult]][[PromiseIsHandled]][[PromiseFulfillReactions]] 和 [[PromiseRejectReactions]]

          • 創(chuàng)建一個(gè) Promise 能力記錄。這個(gè)記錄 “封裝” 了 Promise,并增加了額外的功能來(lái) resolve 或 reject promise。這些功能可控制 promise 的最終 [[PromiseState]] 和 [[PromiseResult]] ,并啟動(dòng)異步任務(wù)。

          我們可以通過(guò)調(diào)用 resolve 來(lái)解決這個(gè) Promise,這是通過(guò)執(zhí)行函數(shù)可以實(shí)現(xiàn)的。當(dāng)我們調(diào)用 resolve 時(shí):

          • [[PromiseState]] 被設(shè)置為 “已實(shí)現(xiàn)”(fulfilled)。

          • [[PromiseResult]] 被設(shè)置為我們傳遞給 resolve 的值,在這種情況下是 “完成!”(Done!)。

          調(diào)用 reject 時(shí)的過(guò)程類(lèi)似,現(xiàn)在 [[PromiseState]] 被設(shè)置為 “已拒絕”(rejected),并且 [[PromiseResult]] 被設(shè)置為我們傳遞給 reject 的值,這是 “失敗!”(Fail!)。

          當(dāng)然很好。但是,使用函數(shù)來(lái)改變對(duì)象內(nèi)部屬性有什么特別的呢?

          答案就在與我們目前跳過(guò)的兩個(gè)內(nèi)部槽相關(guān)的行為中:[[PromiseFulfillReactions]] 和 [[PromiseRejectReactions]]

          [[PromiseFulfillReactions]] 字段包含 Promise Reactions。這是一個(gè)通過(guò)將 then 處理程序鏈接到 Promise 而創(chuàng)建的對(duì)象。

          此 Promise Reaction 包含一個(gè) [[Handler]] 屬性,其中包含我們傳遞給它的回調(diào)。當(dāng) promise resolve 時(shí),該處理程序會(huì)被添加到微任務(wù)隊(duì)列中,并可訪問(wèn) promise 解析時(shí)的值。

          當(dāng) promise 解析時(shí),這個(gè)處理程序接收到 [[PromiseResult]] 的值作為其參數(shù),然后將其推送到 Microtask Queue 微任務(wù)隊(duì)列。

          這就是 promise 的異步部分發(fā)揮作用的地方!

          微任務(wù)隊(duì)列是事件循環(huán)(event loop)中的一個(gè)專(zhuān)門(mén)隊(duì)列。當(dāng)調(diào)用棧(Call Stack)為空時(shí),事件循環(huán)首先處理微任務(wù)隊(duì)列中等待的任務(wù),然后再處理來(lái)自常規(guī)任務(wù)隊(duì)列(也稱(chēng)為 “回調(diào)隊(duì)列” 或 “宏任務(wù)隊(duì)列”)的任務(wù)。

          如果你想了解更多,可以查看我的事件循環(huán)視頻!

          類(lèi)似地,我們可以通過(guò)鏈?zhǔn)?catch 來(lái)創(chuàng)建一個(gè) Promise Reaction 記錄來(lái)處理 Promise Reject。當(dāng) Promise 被拒絕時(shí),這個(gè)回調(diào)會(huì)被添加到微任務(wù)隊(duì)列。

          到目前為止,我們只是在執(zhí)行函數(shù)內(nèi)直接調(diào)用 resolve 或 reject。雖然這是可能的,但它并沒(méi)有充分利用 Promise 的全部功能(和主要目的)!

          在大多數(shù)情況下,我們希望在稍后的某個(gè)時(shí)間點(diǎn)(通常是異步任務(wù)完成時(shí))進(jìn)行 resolve 或 reject。

          異步任務(wù)在主線程之外執(zhí)行,例如讀取文件(如 fs.readFile)、提出網(wǎng)絡(luò)請(qǐng)求(如 https.get 或 XMLHttpRequest),或者像定時(shí)器(setTimeout)這樣簡(jiǎn)單的任務(wù)。

          當(dāng)這些任務(wù)在未來(lái)某個(gè)未知的時(shí)間點(diǎn)完成時(shí),我們可以使用此類(lèi)異步操作通常提供的回調(diào)功能,要么使用異步任務(wù)返回的數(shù)據(jù)進(jìn)行 resolve,要么在發(fā)生錯(cuò)誤時(shí)進(jìn)行 reject。

          為了直觀地說(shuō)明這一點(diǎn),讓我們一步步來(lái)執(zhí)行。為了讓這個(gè)演示簡(jiǎn)單但仍然真實(shí),我們將使用 setTimeout 來(lái)添加一些異步行為。

           new Promise((resolve) => {
          setTimeout(() => resolve("Done!"), 100);
          }).then(result => console.log(result))

          首先,將 new Promise 構(gòu)造函數(shù)添加到調(diào)用棧,并創(chuàng)建 Promise 對(duì)象。

          然后,執(zhí)行函數(shù)被執(zhí)行。在函數(shù)體的第一行,我們調(diào)用了 setTimeout,并將其添加到調(diào)用堆棧中。

          setTimeout 負(fù)責(zé)在 Timers Web API 中調(diào)度計(jì)時(shí)器,延遲時(shí)間為 100 毫秒,之后我們傳遞給 setTimeout 的回調(diào)將被推送到任務(wù)隊(duì)列。

          這里的異步行為與 setTimeout 有關(guān),與 promise 無(wú)關(guān)。我在這里展示這個(gè)是為了展示承諾的常見(jiàn)用法 —— 在一些延遲后解決一個(gè) promise。

          然而,延遲本身并不是由 promise 引起的。promise 被設(shè)計(jì)為與異步操作一起工作,但這些異步操作可以來(lái)自不同的來(lái)源,如定時(shí)器或網(wǎng)絡(luò)請(qǐng)求。

          在定時(shí)器和構(gòu)造函數(shù)從調(diào)用棧中彈出后,引擎遇到了 then。

          then 被添加到調(diào)用棧,并創(chuàng)建了一個(gè) Promise Reaction 記錄,該處理程序就是我們作為回調(diào)傳遞給 then 處理程序的代碼。

          由于 [[PromiseState]] 仍然是 “掛起”(pending),這個(gè) Promise Reaction 記錄會(huì)被添加到 [[PromiseFulfillReactions]] 列表中。

          100 毫秒過(guò)后,setTimeout 回調(diào)被推送到任務(wù)隊(duì)列。

          腳本已經(jīng)運(yùn)行完畢,因此調(diào)用棧為空,這意味著該任務(wù)現(xiàn)在是從 Task Queue 中取出放到 Call Stack 上,它調(diào)用了 resolve。

          調(diào)用 resolve 將 [[PromiseState]] 設(shè)置為 “fulfilled”,將 [[PromiseResult]] 設(shè)置為 “Done!”,并與 Promise Reaction 處理程序相關(guān)的代碼被添加到 Microtask Queue 中。

          resolve 和回調(diào)從調(diào)用棧中彈出。

          由于調(diào)用棧為空,事件循環(huán)首先檢查微任務(wù)隊(duì)列,那里 then 處理程序的回調(diào)正在等待。

          回調(diào)現(xiàn)在被添加到調(diào)用棧,并記錄 result 的值,即 [[PromiseResult]] 的值;字符串 "Done!"。

          一旦回調(diào)執(zhí)行完畢并從調(diào)用棧中彈出,程序就完成了!

          除了創(chuàng)建一個(gè) Promise Reaction 之外,then 還返回一個(gè) Promise。這意味著我們可以將多個(gè) then 鏈接在一起,例如:

           new Promise((resolve) => {
          resolve(1);
          })
          .then(result => result * 2)
          .then(result => result * 2)
          .then(result => console.log(result));

          執(zhí)行這段代碼時(shí),在調(diào)用 Promise 構(gòu)造函數(shù)時(shí)會(huì)創(chuàng)建一個(gè) Promise 對(duì)象。之后,每當(dāng)引擎遇到 then 時(shí),就會(huì)創(chuàng)建一個(gè) Promise Reaction 記錄和一個(gè) Promise Object。

          在這兩種情況下,then 的回調(diào)都將接收到的 [[PromiseResult]] 值乘以 2。then 的 [[PromiseResult]] 被設(shè)置為計(jì)算的結(jié)果,這個(gè)結(jié)果又被下一個(gè) then 的處理程序使用。

          最終,結(jié)果被記錄下來(lái)。由于我們沒(méi)有顯式地返回一個(gè)值,所以最后一個(gè) then promise 的 [[PromiseResult]] 是未定義的,這意味著它隱式地返回了未定義的值。

          當(dāng)然,使用數(shù)字并不是最現(xiàn)實(shí)的場(chǎng)景。相反,您可能希望逐步改變 promise 的結(jié)果,就像逐步改變圖片的外觀一樣。

          例如,您可能希望采取一系列增量的步驟,通過(guò)操作(如調(diào)整大小、應(yīng)用濾鏡、添加水印等)來(lái)改變圖像的外觀。

           function loadImage(src) {
          return new Promise((resolve, reject) => {
          const img = new Image();
          img.onload = () => resolve(img);
          img.onerror = reject;
          img.src = src;
          });
          }
          loadImage(src)
          .then(image => resizeImage(image))
          .then(image => applyGrayscaleFilter(image))
          .then(image => addWatermark(image))

          這類(lèi)型的任務(wù)通常涉及異步操作,這使得 promise 成為以非阻塞方式管理這些操作的良好選擇。

          結(jié)論

          長(zhǎng)話短說(shuō),Promise 只是具有一些額外功能來(lái)改變其內(nèi)部狀態(tài)的對(duì)象。

          Promises 最酷的地方在于,如果通過(guò) then 或 catch 附加了處理程序,就可以觸發(fā)異步操作。由于處理程序被推送到微任務(wù)隊(duì)列,因此可以以非阻塞的方式處理最終結(jié)果。這樣就能更輕松地處理錯(cuò)誤、將多個(gè)操作連鎖在一起,并使代碼更具可讀性和可維護(hù)性!

          Promise 然是一個(gè)基礎(chǔ)概念,對(duì)每個(gè) JavaScript 開(kāi)發(fā)人員來(lái)說(shuō)都很重要。如果您有興趣了解更多,async/await 語(yǔ)法(承諾的語(yǔ)法糖)等其他特性以及 Async Generators(異步生成器)等特性將為異步代碼的使用提供更多方法。

          關(guān)于本文
          譯者:@飄飄
          作者:@Lydia Hallie
          原文:https://www.lydiahallie.com/blog/promise-execution

          最后

          Node 社群

             


          我組建了一個(gè)氛圍特別好的 Node.js 社群,里面有很多 Node.js小伙伴,如果你對(duì)Node.js學(xué)習(xí)感興趣的話(后續(xù)有計(jì)劃也可以),我們可以一起進(jìn)行Node.js相關(guān)的交流、學(xué)習(xí)、共建。下方加 考拉 好友回復(fù)「Node」即可。

             “分享、點(diǎn)贊在看” 支持一下

          瀏覽 223
          2點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          2點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  操逼视频免费看 | 神马午夜97 | 一本大道久久无码精品一区二区三区 | 狠狠撸狠狠操 | 操色婷婷 |