<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      [NodeJS] NodeJS事件循環

      JS是單線程的,如果出現阻塞會嚴重影響代碼執行效率。NodeJS通過事件循環,盡可能地將耗時任務委派給系統內核來實現非阻塞IO。

      NodeJS提供了許多和異步相關的API,除了語言標準規定的setTimeoutsetInterval,還有setImmediateprocess.nextTick

      經常和這幾個出現在面試題里的還有Promise.resolve().then()

      事件循環流程

      當NodeJS啟動時,會先進行事件循環的初始化(事件循環還沒開始),會先完成下面的事情:

      1. 解析執行同步任務;
      2. 發出異步請求;
      3. 注冊定時器回調;
      4. 執行process.nextTick()

      然后再開始事件循環。

      事件循環的操作順序如下圖所示:

         ┌───────────────────────────┐
      ┌─>│           timers          │
      │  └─────────────┬─────────────┘
      │  ┌─────────────┴─────────────┐
      │  │   pending I/O callbacks   │
      │  └─────────────┬─────────────┘
      │  ┌─────────────┴─────────────┐
      │  │       idle, prepare       │
      │  └─────────────┬─────────────┘      ┌───────────────┐
      │  ┌─────────────┴─────────────┐      │   incoming:   │
      │  │           poll            │<─────┤  connections, │
      │  └─────────────┬─────────────┘      │   data, etc.  │
      │  ┌─────────────┴─────────────┐      └───────────────┘
      │  │           check           │
      │  └─────────────┬─────────────┘
      │  ┌─────────────┴─────────────┐
      └──┤      close callbacks      │
         └───────────────────────────┘
      

      每一個方框對對應著事件循環的一個階段(phase),每一個階段有一個先進先出的回調隊列需要執行。

      當事件循環進入到其中一個階段時,它會依次執行并嘗試清空隊列中的回調任務,當隊列被清空或者回調執行數量達到最大限制時,事件循環會進入到下一個階段。

      1. timers:定時器階段,執行setTimeoutsetInterval的回調函數;
      2. pending I/O callbacks:除了定時器回調、setImmediate回調和關閉回調,其它回調都在這里執行;
      3. idle, prepare:這個階段只供libuv內部調用;
      4. Poll:這個階段是輪詢時間,用于等待還未返回的 I/O 事件,比如服務器的回應、用戶移動鼠標等等。這個階段的時間會比較長。如果沒有其他異步任務要處理(比如到期的定時器),會一直停留在這個階段,等待 I/O 請求返回結果。
      5. check:執行setImmediate回調;
      6. close callbacks:執行關閉請求的回調函數,比如socket.on('close', ...)

      事件循環的源碼解析

      源碼位置:

      int uv_run(uv_loop_t* loop, uv_run_mode mode) {
        int timeout;
        int r;
        int can_sleep;
      
        // 檢查事件循環是否還活躍(即是否還有活躍的句柄或請求)
        r = uv__loop_alive(loop);
        if (!r)
          uv__update_time(loop); // 更新事件循環的當前時間
      
        /* 保持向后兼容性,在進入 UV_RUN_DEFAULT 的 while 循環之前處理定時器。
         * 否則定時器只需執行一次,這應在輪詢之后完成,以保持事件循環的正確執行順序。
         */
        if (mode == UV_RUN_DEFAULT && r != 0 && loop->stop_flag == 0) {
          uv__update_time(loop); // 更新事件循環的當前時間
          uv__run_timers(loop);  // 運行所有到期的定時器 (Timers)
        }
      
        // 主循環,根據不同的模式執行事件循環
        while (r != 0 && loop->stop_flag == 0) {
          // 檢查是否可以進入睡眠狀態,即是否有掛起的任務或空閑句柄
          can_sleep =
              uv__queue_empty(&loop->pending_queue) &&
              uv__queue_empty(&loop->idle_handles);
      
          // 運行掛起的任務 (Pending Callbacks)
          uv__run_pending(loop);
          // 運行空閑句柄和預處理句柄 (Idle Prepare)
          uv__run_idle(loop);
          uv__run_prepare(loop);
      
          timeout = 0;
          // 根據模式設置超時時間
          if ((mode == UV_RUN_ONCE && can_sleep) || mode == UV_RUN_DEFAULT)
            timeout = uv__backend_timeout(loop);
      
          // 增加事件循環計數
          uv__metrics_inc_loop_count(loop);
      
          // 輪詢I/O事件 (Poll)
          uv__io_poll(loop, timeout);
      
          /* 處理立即回調(例如 write_cb)固定次數,以避免循環饑餓。 */
          for (r = 0; r < 8 && !uv__queue_empty(&loop->pending_queue); r++)
            uv__run_pending(loop);
      
          /*
           * 進行最后一次 provider_idle_time 的更新,以防 uv__io_poll
           * 因超時返回但未接收到任何事件。如果 provider_entry_time 從未設置
           * (即 timeout == 0),或者已經因為接收到事件而更新,則此調用將被忽略。
           */
          uv__metrics_update_idle_time(loop);
      
          // 運行check句柄 (Check)
          uv__run_check(loop);
          // 運行關閉的回調 (Close Callbacks)
          uv__run_closing_handles(loop);
      
          // 更新事件循環的當前時間和運行所有到期的定時器 (Timers)
          uv__update_time(loop);
          uv__run_timers(loop);
      
          // 檢查事件循環是否還活躍
          r = uv__loop_alive(loop);
          if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT)
            break; // 如果模式為 UV_RUN_ONCE 或 UV_RUN_NOWAIT,則退出循環
        }
      
        /* 這個 if 語句讓 gcc 將其編譯為條件存儲。避免弄臟緩存行。 */
        if (loop->stop_flag != 0)
          loop->stop_flag = 0; // 清除停止標志
      
        return r; // 返回事件循環是否還活著
      }
      

      名詞解釋

      • 條件存儲:條件存儲是一種優化技術。編譯器可以將 if 語句編譯成一種條件存儲操作。這種操作僅在特定條件下才會寫入數據,從而避免不必要的寫操作。在這段代碼中,loop->stop_flag 的值只有在其當前值不為零時才會被修改。這避免了不必要的寫操作,因為如果 loop->stop_flag 已經是零,則不需要再寫一次零。

      • 緩存行:緩存行是處理器緩存的基本單位,通常為 64 字節。緩存用于存儲從內存中加載的數據,以加快訪問速度。當處理器需要訪問某個內存地址時,會先檢查緩存中是否存在對應的數據。如果緩存中存在該數據(稱為緩存命中),則可以快速訪問;如果不存在(稱為緩存未命中),則需要從較慢的主存中加載數據。在現代處理器中,緩存寫操作可能會使緩存行變臟(dirty),即緩存中的數據與主存中的數據不一致。每次寫操作都可能導致緩存行的變臟和隨后的寫回操作(將緩存中的數據寫回主存),這些操作會影響性能。

      通過條件存儲,如果 loop->stop_flag 本來就是零,則不會進行寫操作,避免了緩存行變臟,從而減少了寫回主存的開銷,提高了緩存的利用效率。

      process.nextTick和Promise

      或許你會疑惑上面的事件循環階段怎么沒有講到process.nextTickPromise回調(微任務)。

      這兩個回調的執行時機不在階段“內部”,而是在階段“之間”,在每個階段結束時被執行。

      并且,process.nextTick的執行順序先于Promise回調(微任務)。

      微任務除了nextTickpromise,還有MutationObserverqueueMicrotask

      nextTick屬于特殊的高優先級微任務,而promiseMutationObserverqueueMicrotask的優先級一致。

      MutationObserver是用來監聽DOM的,是瀏覽器獨有的;而nextTickNodeJS獨有的;

      promisequeueMicrotask在兩種環境下都有。

      setTimeout和setImmediate

      setTimeouttimers階段執行,setImmediate的回調在check階段執行,因此setTimeout會早于setImmediate完成。

      案例

      setTimeout(()=>console.log(1));
      setImmediate(()=>console.log(2));
      

      理論上上面這段代碼會先輸出1再輸出2,但實際是順序不確定。

      因為在NodeJS中,setTimeout的第二個參數delay缺省值為1,根據官方文檔,這個參數的取值范圍為12147483647之間,超出這個范圍會被設置為1,而非整數會被截去小數部分變為整數。

      并且實際執行的時候,進入事件循環之后,可能到了1毫秒,也可能還沒到,因此timers階段的隊列可能是空的,于是就先執行了check階段的setImmediate回調,而到了下一階段,才是setTimeout的回調。

      另一個案例

      const fs = require('fs');
      
      fs.readFile('test.js', () => {
        setTimeout(() => console.log(1));
        setImmediate(() => console.log(2));
      });
      

      這個例子中,則一定是先輸出2,然后才是1.

      因為readFile的回調會在pending I/O callbacks階段被執行,此時的setTimeout回調最快也只能在下一個loop中被執行,而setImmediate的回調被添加到check階段的隊列,當當前這個loop執行到check階段的時候,就會被執行。

      測試題

      setImmediate(() => {
        console.log(1)
        setTimeout(() => {
          console.log(2)
        }, 100)
        setImmediate(() => {
          console.log(3)
        })
        process.nextTick(() => {
          console.log(4)
        })
      })
      process.nextTick(() => {
        console.log(5)
        setTimeout(() => {
          console.log(6)
        }, 100)
        setImmediate(() => {
          console.log(7)
        })
        process.nextTick(() => {
          console.log(8)
        })
      })
      console.log(9)
      

      答案

      往下滑
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      9 5 8 1 4 7 3 6 2
      

      解析

      1. 同步代碼:注冊setImmediate,等待事件循環到達check階段;注冊nextTick回調;同步代碼輸出9
      2. 事件循環啟動后,在到達check階段之前nextTick肯定是先被執行的,于是先輸出5;輸出之后依次注冊setTimeoutsetImmediatenextTick
      3. 在到達check階段之前的階段之間,nextTick回調被再次執行,輸出8
      4. 中間階段的隊列都是空的,直到事件循環來到check階段,執行最頂層的setImmediate回調,先輸出1,然后依次注冊setTimeoutsetImmediatenextTick回調;
      5. 離開setImmediate,再次執行nextTick回調,輸出4
      6. 到達timers階段,但是通常這時候還沒到達100ms,于是跳過;
      7. 再次到達check階段,輸出隊列中的73
      8. 在下次循環的poll階段等待,直到定時器完成,依次輸出62

      參考文章

      [1] Node 定時器詳解 - 阮一峰的網絡日志
      [2] The Node.js Event Loop
      [3] Understanding process.nextTick()
      [4] Understanding setImmediate()

      posted @ 2024-07-04 17:40  feixianxing  閱讀(147)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 亚洲VA中文字幕无码久久| 亚洲国产精品毛片在线看| 日韩人妻系列无码专区| 一区二区三区四区五区自拍| 国产又大又黑又粗免费视频| 精品一区二区三区在线观看l| 亚洲国产日韩欧美一区二区三区 | 国产精品一区二区AV| 成年女人免费碰碰视频| 最新精品国产自偷在自线| 成人资源网亚洲精品在线| 国产老妇伦国产熟女老妇高清| 人人玩人人添人人澡超碰| 国产成人一区二区免av| 中文国产不卡一区二区| 在线看av一区二区三区| 人成午夜免费视频无码| 在线看免费无码av天堂的| 四虎精品国产精品亚洲精| 国产精品爽黄69天堂A| 国产欧美日韩精品a在线观看| 国产乱子影视频上线免费观看| 亚洲国产一区二区三区最新| 国产精品亚洲а∨天堂2021| 麻豆亚州无矿码专区视频| 国产精品十八禁在线观看| 国产啪视频免费观看视频| 亚洲精品国模一区二区| 久久久久四虎精品免费入口| 手机无码人妻一区二区三区免费| 国产亚洲精品在av| 国产免费午夜福利757| 99在线精品视频观看免费| 亚洲人成小说网站色在线| 久久综合亚洲鲁鲁九月天| 熟女亚洲综合精品伊人久久| 四虎影视一区二区精品| 又污又爽又黄的网站| 国精品午夜福利不卡视频| 鲁丝片一区二区三区免费| 亚洲跨种族黑人xxxxx|