成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Node.js 的微任務(wù)處理(基于Node.js V17)

開發(fā) 前端
Node.js 的事件循環(huán)已經(jīng)老生常談,但是在 Node.js 的執(zhí)行流程中,事件循環(huán)并不是全部,在事件循環(huán)之外,微任務(wù)的處理也是核心節(jié)點(diǎn).

[[441996]]

前言:Node.js 的事件循環(huán)已經(jīng)老生常談,但是在 Node.js 的執(zhí)行流程中,事件循環(huán)并不是全部,在事件循環(huán)之外,微任務(wù)的處理也是核心節(jié)點(diǎn),比如 nextTick 和 Promise 任務(wù)的處理。本文介紹 Node.js 中微任務(wù)處理的相關(guān)內(nèi)容。網(wǎng)上文章和很多面試題中有很多關(guān)于 Promise、nextTick、setTimeout 和 setImmediate 執(zhí)行順序的內(nèi)容。通過本文,讓你從原理上理解他們,碰到相關(guān)的問題就引刃而解,不再拘泥于背誦和記錄。

1 事件循環(huán)

本文不打算詳細(xì)地講解事件循環(huán),因?yàn)橐呀?jīng)有很多相關(guān)文章,而且本身也不是很復(fù)雜的流程。事件循環(huán)本質(zhì)上是一個(gè)消費(fèi)者和生產(chǎn)者的模型,我們可以理解事件循環(huán)的每一個(gè)階段都維護(hù)了一個(gè)任務(wù)隊(duì)列,然后在事件循環(huán)的每一輪里就會(huì)去消費(fèi)這些任務(wù),那就是執(zhí)行回調(diào),然后在回調(diào)里又可以生產(chǎn)任務(wù),從而驅(qū)動(dòng)整個(gè)事件循環(huán)的運(yùn)行。當(dāng)事件循環(huán)里沒有生產(chǎn)者的時(shí)候,系統(tǒng)就會(huì)退出。而有些生產(chǎn)者會(huì) hold 住事件循環(huán)從而讓整個(gè)系統(tǒng)不會(huì)退出,比如我們啟動(dòng)了一個(gè) TCP 服務(wù)器。事件循環(huán)處理了 Node.js 中大部分的執(zhí)行流程,但是并不是全部。

2 微任務(wù)

Node.js 中,典型的微任務(wù)包括 nexiTick 和 Promise。官網(wǎng)說 nextTick 任務(wù)會(huì)在繼續(xù)事件循環(huán)之前被處理,描述得比較宏觀,下面我們來看一下具體的實(shí)現(xiàn)細(xì)節(jié)。微任務(wù)的處理時(shí)機(jī)分為兩個(gè)時(shí)間點(diǎn)。1. 定義 C++ InternalCallbackScope 對(duì)象,在對(duì)象析構(gòu)時(shí)。2. 主動(dòng)調(diào) JS 函數(shù) runNextTicks。

2.1 InternalCallbackScope

下面先看一下 InternalCallbackScope。通常在需要處理微任務(wù)的地方定義一個(gè) InternalCallbackScope 對(duì)象,然后執(zhí)行一些其他的代碼,最后退出作用域。

  1.     InternalCallbackScope scope 
  2.     // some code 
  3.  
  4. } // 退出作用域,析構(gòu) 

下面看一下 InternalCallbackScope 析構(gòu)函數(shù)的邏輯。

  1. InternalCallbackScope::~InternalCallbackScope() { 
  2.   Close(); 
  3.  
  4.  
  5. void InternalCallbackScope::Close() { 
  6.  
  7.  tick_callback->Call(context, process, 0, nullptr); 
  8.  

在析構(gòu)函數(shù)里會(huì)執(zhí)行 tick_callback 函數(shù)。我們看看這個(gè)函數(shù)是什么。

  1. static void SetTickCallback(const FunctionCallbackInfo<Value>& args) { 
  2.   Environment* env = Environment::GetCurrent(args); 
  3.   CHECK(args[0]->IsFunction()); 
  4.   env->set_tick_callback_function(args[0].As<Function>()); 
  5.  

tick_callback 是由 SetTickCallback 設(shè)置的。

  1. setTickCallback(processTicksAndRejections); 

我們可以看到通過 setTickCallback 設(shè)置的這個(gè)函數(shù)是 processTicksAndRejections。

  1. function processTicksAndRejections() { 
  2.   let tock; 
  3.   do { 
  4.     while (tock = queue.shift()) { 
  5.       const callback = tock.callback; 
  6.       callback(); 
  7.     } 
  8.     runMicrotasks(); 
  9.   } while (!queue.isEmpty() || processPromiseRejections()); 
  10.  

processTicksAndRejections 正是處理微任務(wù)的函數(shù),包括 tick 和 Promise 任務(wù)。現(xiàn)在我們已經(jīng)了解了 InternalCallbackScope 對(duì)象的邏輯。那么下面我們來看一下哪里使用了這個(gè)對(duì)象。第一個(gè)地方是在 Node.js 初始化時(shí),執(zhí)行完用戶 JS 后,進(jìn)入事件循環(huán)前。看看相關(guān)代碼。

我們看到在 Node.js 初始化時(shí),執(zhí)行用戶 JS 后,進(jìn)入事件循環(huán)前會(huì)處理一次微任務(wù),所以我們?cè)谧约旱某跏蓟?JS 里調(diào)用了 nextTick 的話,就會(huì)在這時(shí)候被處理。第二個(gè)地方是每次從 C、C++ 層執(zhí)行 JS 層回調(diào)時(shí)。

  1. MaybeLocal<Value> AsyncWrap::MakeCallback(const Local<Function> cb, 
  2.                                           int argc, 
  3.                                           Local<Value>* argv) { 
  4.   ProviderType provider = provider_type(); 
  5.   async_context context { get_async_id(), get_trigger_async_id() }; 
  6.   MaybeLocal<Value> ret = InternalMakeCallback( 
  7.       env(), object(), object(), cb, argc, argv, context); 
  8.   return ret; 
  9.  

MakeCallback 是 C、C++ 層回調(diào) JS 層的函數(shù),這個(gè)函數(shù)里又調(diào)用一個(gè) InternalMakeCallback。

  1. MaybeLocal<Value> InternalMakeCallback(Environment* env, 
  2.                                        Local<Object> resource, 
  3.                                        Local<Object> recv, 
  4.                                        const Local<Function> callback, 
  5.                                        int argc, 
  6.                                        Local<Value> argv[], 
  7.                                        async_context asyncContext) { 
  8.  
  9.   // 定義 InternalCallbackScope 
  10.   InternalCallbackScope scope(env, resource, asyncContext, flags); 
  11.   // 執(zhí)行 JS 層回調(diào) 
  12.   callback->Call(context, recv, argc, argv); 
  13.   // 處理微任務(wù) 
  14.   scope.Close(); 
  15.  

我們看到 InternalMakeCallback 里定義了一個(gè) InternalCallbackScope,然后在回調(diào)完 JS 函數(shù)后會(huì)調(diào)用 InternalCallbackScope 對(duì)象的 Close 進(jìn)行微任務(wù)的處理。

以上是典型的處理時(shí)機(jī)。另外在某些地方也會(huì)定義 InternalCallbackScope 對(duì)象,具體可在源碼里搜索。

2.2 runNextTicks

剛才介紹了每次事件循環(huán)消費(fèi)任務(wù)時(shí),就會(huì)去遍歷每一個(gè)階段的任務(wù)隊(duì)列,然后逐個(gè)執(zhí)行任務(wù)節(jié)點(diǎn)對(duì)應(yīng)的回調(diào)。執(zhí)行回調(diào)的時(shí)候,就會(huì)從 C 到 C++ 層,然后再到 JS 層,執(zhí)行完 JS 代碼后,會(huì)再次回調(diào) C++ 層,C++ 層會(huì)進(jìn)行一次微任務(wù)的處理,處理完后再回到 C 層,繼續(xù)執(zhí)行下一個(gè)任務(wù)節(jié)點(diǎn)的回調(diào),以此類推。這看起來覆蓋了所有的情況,但是有兩個(gè)地方比較特殊,那就是 setTimeout 和 setImmediate。其他的任務(wù)都是一個(gè)節(jié)點(diǎn)對(duì)應(yīng)一個(gè) C、C++ 和 JS 回調(diào),所以如果在 JS 回調(diào)里產(chǎn)生的微任務(wù),在回到 C++ 層的時(shí)候就會(huì)被處理。但是為了提高性能,Node.js 的定時(shí)器和 setImmediate 在實(shí)現(xiàn)上是一個(gè)底層節(jié)點(diǎn)管理多個(gè) JS 回調(diào)。這里以定時(shí)器為例,Node.js 在底層使用了一個(gè) Libuv 的定時(shí)器節(jié)點(diǎn)管理 JS 層的所有定時(shí)器,并在 JS 層里維護(hù)了所有的定時(shí)器節(jié)點(diǎn),然后把 Libuv 定時(shí)節(jié)點(diǎn)的超時(shí)時(shí)間設(shè)置為 JS 層最快到期的節(jié)點(diǎn)的時(shí)間,這樣就會(huì)帶來一個(gè)問題。就是當(dāng)有定時(shí)器超時(shí),Libuv 從 C、C++ 回調(diào) JS 層時(shí),JS 層會(huì)直接處理所有的超時(shí)節(jié)點(diǎn)后再回到 C++ 層,這時(shí)候才有機(jī)會(huì)處理微任務(wù)。這會(huì)導(dǎo)致 setTimeout 里產(chǎn)生的微任務(wù)沒有在宏任務(wù)(setTimeout 的回調(diào))執(zhí)行完后被處理。這就不符合規(guī)范了。所以這個(gè)地方還需要特殊處理一下。我們看看相關(guān)的代碼。

  1. function processTimers(now) { 
  2.     nextExpiry = Infinity; 
  3.     let list; 
  4.     let ranAtLeastOneList = false
  5.     while (list = timerListQueue.peek()) { 
  6.       if (list.expiry > now) { 
  7.         nextExpiry = list.expiry; 
  8.         return refCount > 0 ? nextExpiry : -nextExpiry; 
  9.       } 
  10.       // 處理 listOnTimeout 最后一個(gè)回調(diào)里產(chǎn)生的微任務(wù) 
  11.       if (ranAtLeastOneList) 
  12.         runNextTicks(); 
  13.       else 
  14.         ranAtLeastOneList = true
  15.       listOnTimeout(list, now); 
  16.     } 
  17.     return 0; 
  18.  
  19. function listOnTimeout(list, now) { 
  20.     let ranAtLeastOneTimer = false
  21.     let timer; 
  22.     while (timer = L.peek(list)) { 
  23.       // 處理微任務(wù) 
  24.       if (ranAtLeastOneTimer) 
  25.         runNextTicks(); 
  26.       else 
  27.         ranAtLeastOneTimer = true
  28.       // 執(zhí)行 setTimeout 回調(diào) 
  29.       timer._onTimeout(); 
  30.     } 
  31.  

定時(shí)器的架構(gòu)如下。

Node.js 在 JS 層維護(hù)了一個(gè)樹,每個(gè)節(jié)點(diǎn)管理一個(gè)列表,處理超時(shí)事件時(shí),就會(huì)遍歷這棵樹的每個(gè)節(jié)點(diǎn),然后再遍歷這個(gè)節(jié)點(diǎn)對(duì)應(yīng)隊(duì)列里的每個(gè)節(jié)點(diǎn)。而上面的代碼就是保證在每次調(diào)用完一個(gè) setTimeout 回調(diào)時(shí),都會(huì)處理一次微任務(wù)。同樣 setImmediate 任務(wù)也是類似的。

  1. let ranAtLeastOneImmediate = false
  2.  while (immediate !== null) { 
  3.    if (ranAtLeastOneImmediate) 
  4.      runNextTicks(); 
  5.    else 
  6.      ranAtLeastOneImmediate = true
  7.  
  8.   immediate._onImmediate(); 
  9.   immediate = immediate._idleNext; 
  10.  } 

以上的補(bǔ)償處理就保證了宏任務(wù)和微任務(wù)的處理能符合預(yù)期。

 

責(zé)任編輯:姜華 來源: 編程雜技
相關(guān)推薦

2021-10-26 06:43:36

NodeJavaScript引擎

2013-11-01 09:34:56

Node.js技術(shù)

2020-10-26 08:34:13

Node.jsCORS前端

2015-03-10 10:59:18

Node.js開發(fā)指南基礎(chǔ)介紹

2011-09-08 13:46:14

node.js

2011-11-01 10:30:36

Node.js

2011-09-02 14:47:48

Node

2011-09-09 14:23:13

Node.js

2011-11-10 08:55:00

Node.js

2012-10-24 14:56:30

IBMdw

2020-05-29 15:33:28

Node.js框架JavaScript

2012-02-03 09:25:39

Node.js

2022-12-02 23:20:06

Node.jsC++任務(wù)管理

2013-04-12 01:51:08

微信公眾平臺(tái)接口開發(fā)

2023-10-04 07:35:03

2015-06-23 15:27:53

HproseNode.js

2021-04-06 10:15:29

Node.jsHooks前端

2024-07-08 08:53:52

2021-02-01 15:42:45

Node.jsSQL應(yīng)用程序

2021-07-09 00:24:10

No.jsNode.js原理
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 天天操夜夜操 | 欧美日韩成人一区二区 | 夜夜艹 | 久久久精品在线 | 亚洲欧美激情四射 | 亚洲精品一区二区三区在线观看 | 夜夜操天天艹 | 91精品导航 | av毛片在线播放 | 日韩av资源站 | 在线免费观看色 | 在线日韩av电影 | 色视频在线播放 | 日韩亚洲一区二区 | 国产情侣在线看 | 亚洲最大看片网站 | 国产美女自拍视频 | 青草青草久热精品视频在线观看 | wwww.8888久久爱站网 | 国产精品一区二区三区四区 | 亚洲免费精品 | 亚洲精品高清视频 | 免费在线看黄视频 | 亚洲欧美视频 | 欧美激情在线播放 | 一本在线 | 欧美一区二区三区在线观看视频 | 久久久123 | 国产成人精品午夜视频免费 | 日本精品一区二区三区视频 | 久久躁日日躁aaaaxxxx | 欧美成人a∨高清免费观看 老司机午夜性大片 | 国产伦精品一区二区三区照片91 | 黄片毛片免费观看 | 欧美精品一区二区三区一线天视频 | 国产人久久人人人人爽 | 精品91视频 | 久久久久久亚洲 | 亚洲欧美中文日韩在线v日本 | 成人在线观看亚洲 | 91精品一区二区 |