亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > JS > 正文

深入淺析Node.js 事件循環、定時器和process.nextTick()

2024-05-06 16:46:32
字體:
來源:轉載
供稿:網友

什么是事件循環

盡管JavaScript是單線程的,但通過盡可能將操作放到系統內核執行,事件循環允許Node.js執行非阻塞I/O操作。

由于現代大多數內核都是多線程的,因此它們可以處理在后臺執行的多個操作。 當其中一個操作完成時,內核會告訴Node.js,以便可以將相應的回調添加到 輪詢隊列 中以最終執行。 我們將在本主題后面進一步詳細解釋。

事件循環解釋

當Node.js啟動時,它初始化事件循環,處理提供的輸入腳本(或放入 REPL ,本文檔未涉及),這可能會進行異步API調用,調度計時器或調用 process.nextTick() , 然后開始處理事件循環。

下圖顯示了事件循環操作順序的簡要概述。

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

注意:每個框都將被稱為事件循環的“階段”。

每個階段都要執行一個FIFO的回調隊列。 雖然每個階段都有其特殊的方式,但通常,當事件循環進入給定階段時,它將執行特定于該階段的任何操作,然后在該階段的隊列中執行回調,直到隊列耗盡或最大回調數量為止 。 當隊列耗盡或達到回調限制時,事件循環將移至下一階段,依此類推。

由于這些操作中的任何一個可以調度更多操作并且在輪詢階段中處理的新事件由內核排隊,因此輪詢事件可以在處理輪詢事件時排隊。 因此,長時間運行的回調可以允許輪詢階段運行的時間比計時器的閾值長得多。 有關詳細信息,請參閱和部分。

注意:Windows和Unix / Linux實現之間存在輕微差異,但這對于此演示并不重要。 最重要的部分在這里。 實際上有七到八個步驟,但我們關心的是 - Node.js實際使用的那些 - 是上面那些。

階段概述

  • timer : 此階段執行 setTimeout() 和 setInterval() 調度的回調
  • pending callbacks : 執行延遲到下一個循環迭代的I/O回調
  • idle, prepare : 只用于內部
  • poll : 檢索新的I/O事件; 執行與I/O相關的回調(幾乎所有回調都是帶有異常的 close callbacks , timers 和 setImmediate() 調度的回調); node將在適當的時候阻塞在這里
  • check : 這里調用 setImmediate() 回調函數
  • close callbacks : 一些 close callbacks, 例如. socket.on(‘close', …)

在事件循環的每次運行之間,Node.js檢查它是否在等待任何異步I / O或定時器,如果沒有,則關閉。

階段細節

定時器(timer)

計時器在一個回調執行完之后指定閾值,而不是人們希望的確切時間去執行。 定時器回調將在指定的時間過去后盡早安排; 但是,操作系統調度或其他回調的運行可能會延遲它們。

注意:從技術上講,控制何時執行定時器。

例如,假設您計劃在100毫秒后執行 timeout ,然后您的腳本將異步讀取一個耗時95毫秒的文件:

const fs = require('fs');function someAsyncOperation(callback) { // Assume this takes 95ms to complete fs.readFile('/path/to/file', callback);}const timeoutScheduled = Date.now();setTimeout(() => { const delay = Date.now() - timeoutScheduled; console.log(`${delay}ms have passed since I was scheduled`);}, 100);// do someAsyncOperation which takes 95 ms to completesomeAsyncOperation(() => { const startCallback = Date.now(); // do something that will take 10ms... while (Date.now() - startCallback < 10) { // do nothing }});

當事件循環進入輪詢階段時,它有一個空隊列( fs.readFile() 尚未完成),因此它將等待剩余的ms數,直到達到最快的計時器閾值。 當它等待95毫秒傳遞時, fs.readFile() 完成讀取文件,并且其完成需要10毫秒的回調被添加到輪詢隊列并執行。 當回調結束時,隊列中不再有回調,因此事件循環將看到已達到最快定時器的閾值,然后回繞到定時器階段以執行定時器的回調。 在此示例中,您將看到正在調度的計時器與正在執行的回調之間的總延遲將為105毫秒。

注意:為了防止輪詢階段使事件循環挨餓,libuv(實現Node.js事件循環的C庫和平臺的所有異步行為)在停止輪詢之前也為事件提供了固定的最大值(取決于系統)。

等待回調(pending callbacks)

此階段執行某些系統操作(例如TCP錯誤類型)的回調。 例如,如果TCP套接字在嘗試連接時收到 ECONNREFUSED ,則某些*nix系統希望等待報告錯誤。 這將排隊等待在等待回調階段執行。

輪詢(poll)

輪詢階段有兩個主要功能:

1.計算它阻塞和輪詢I / O的時間,然后
2.處理輪詢隊列中的事件。

當事件循環進入輪詢階段并且沒有定時器調度時,將發生以下兩種情況之一:

  • 如果輪詢隊列不為空,則事件循環將遍歷回調隊列并且同步執行,直到隊列已執行完或者達到系統相關的固定限制。
  • 如果輪詢隊列為空,則會發生以下兩種情況之一:

setImmediate()
setImmediate()

檢查(check)

此階段允許在輪詢階段完成后立即執行回調。 如果輪詢階段變為空閑并且腳本已使用 setImmediate() 排隊,則事件循環可以繼續到檢查階段而不是等待。

setImmediate() 實際上是一個特殊的計時器,它在事件循環的一個單獨階段運行。 它使用libuv API來調度在輪詢階段完成后執行的回調。

通常,在執行代碼時,事件循環最終會到達輪詢階段,它將等待傳入連接,請求等。但是,如果已使用 setImmediate() 調度回調并且輪詢階段變為空閑,則 將結束并繼續檢查階段,而不是等待輪詢事件。

關閉回調(close callbacks)

如果套接字或句柄突然關閉( 例如socket.destroy() ),則在此階段將發出 'close' 事件。 否則它將通過 process.nextTick() 發出。

setImmediate() vs setTimeout()

setImmediate 和 setTimeout() 類似,但根據它們的調用時間以不同的方式運行。

setImmediate()
setTimeout()

執行定時器的順序將根據調用它們的上下文而有所不同。 如果從主模塊中調用兩者,則時間將受到進程性能的限制(可能受到計算機上運行的其他應用程序的影響)。

例如,如果我們運行不在I / O周期內的以下腳本(即主模塊),則執行兩個定時器的順序是不確定的,因為它受進程性能的約束:

// timeout_vs_immediate.jssetTimeout(() => { console.log('timeout');}, 0);setImmediate(() => { console.log('immediate');});$ node timeout_vs_immediate.jstimeoutimmediate$ node timeout_vs_immediate.jsimmediatetimeout

但是,如果在I / O周期內移動兩個調用,則始終首先執行立即回調:

// timeout_vs_immediate.jsconst fs = require('fs');fs.readFile(__filename, () => { setTimeout(() => { console.log('timeout'); }, 0); setImmediate(() => { console.log('immediate'); });});$ node timeout_vs_immediate.jsimmediatetimeout$ node timeout_vs_immediate.jsimmediatetimeout

使用 setImmediate() 而不是 setTimeout() 的主要優點是 setImmediate() 將始終在任何定時器之前執行(如果在I / O周期內調度),與存在多少定時器無關。

process.nextTick()

理解 process.nextTick()

您可能已經注意到 process.nextTick() 沒有顯示在圖中,即使它是異步API的一部分。 這是因為 process.nextTick() 在技術上不是事件循環的一部分。 相反, nextTickQueue 將在當前操作完成后處理,而不管事件循環的當前階段如何。

回顧一下我們的圖表,無論何時在給定階段調用 process.nextTick() ,傳遞給 process.nextTick() 的所有回調都將在事件循環繼續之前得到解決。 這可能會產生一些不好的情況, 因為它允許您通過進行遞歸的 process.nextTick() 調用來“餓死”您的I / O, 這會阻止事件循環到達輪詢階段。

為什么會被允許?

為什么這樣的東西會被包含在Node.js中? 其中一部分是一種設計理念,其中API應該始終是異步的,即使它不是必須的。 以此代碼段為例:

function apiCall(arg, callback) { if (typeof arg !== 'string') return process.nextTick(callback,    new TypeError('argument should be string'));}

這段代碼進行參數檢查,如果不正確,它會將錯誤傳遞給回調。 最近更新的API允許將參數傳遞給 process.nextTick() ,允許它將回調后傳遞的任何參數作為參數傳播到回調,因此您不必嵌套函數。

我們正在做的是將錯誤傳回給用戶,但只有在我們允許其余的用戶代碼執行之后。 通過使用 process.nextTick() ,我們保證 apiCall() 始終在用戶代碼的其余部分之后和允許事件循環繼續之前運行其回調。 為了實現這一點,允許JS調用堆棧展開然后立即執行提供的回調,這允許一個人對 process.nextTick() 進行遞歸調用而不會達到 RangeError :超出v8的最大調用堆棧大小。

這種理念可能會導致一些潛在的問題。 以此片段為例:

let bar;// this has an asynchronous signature, but calls callback synchronouslyfunction someAsyncApiCall(callback) { callback(); }// the callback is called before `someAsyncApiCall` completes.someAsyncApiCall(() => { // since someAsyncApiCall has completed, bar hasn't been assigned any value console.log('bar', bar); // undefined});bar = 1;

用戶將 someAsyncApiCall() 定義為具有異步簽名,但它實際上是同步操作的。 調用它時,在事件循環的同一階段調用提供給 someAsyncApiCall() 的回調,因為 someAsyncApiCall() 實際上不會異步執行任何操作。 因此,回調嘗試引用bar,即使它在范圍內可能沒有該變量,因為該腳本無法運行完成。

通過將回調放在 process.nextTick() 中,腳本仍然能夠運行完成,允許在調用回調之前初始化所有變量,函數等。 它還具有不允許事件循環繼續的優點。 在允許事件循環繼續之前,向用戶警告錯誤可能是有用的。 以下是使用 process.nextTick() 的前一個示例:

let bar;function someAsyncApiCall(callback) { process.nextTick(callback);}someAsyncApiCall(() => { console.log('bar', bar); // 1});bar = 1;

這是另一個真實世界的例子:

const server = net.createServer(() => {}).listen(8080);server.on('listening', () => {});

僅傳遞端口時,端口立即綁定。 因此,可以立即調用 'listening' 回調。 問題是那時候不會設置 .on('listening') 回調。

為了解決這個問題, 'listening' 事件在 nextTick() 中排隊,以允許腳本運行完成。 這允許用戶設置他們想要的任何事件處理程序。

process.nextTick() vs setImmediate()

就用戶而言,我們有兩個類似的調用,但它們的名稱令人困惑。

process.nextTick()setImmediate()

實質上,應該交換名稱。 process.nextTick() 比 setImmediate() 更快地觸發,但這是過去創造的,不太可能改變。 進行此切換會破壞npm上的大部分包。 每天都會添加更多新模塊,這意味著我們每天都在等待,更多的潛在破損發生。 雖然它們令人困惑,但自身的叫法不會改變。

我們建議開發人員在所有情況下都使用 setImmediate() ,因為它更容易推理(并且它導致代碼與更廣泛的環境兼容,如瀏覽器JS。)

為什么要使用 process.nextTick() ?

有兩個主要原因:

  • 允許用戶處理錯誤,清除任何不需要的資源,或者在事件循環繼續之前再次嘗試請求。
  • 有時需要允許回調在調用堆棧展開之后但在事件循環繼續之前運行。

一個例子是匹配用戶的期望。 簡單的例子:

const server = net.createServer();server.on('connection', (conn) => { });server.listen(8080);server.on('listening', () => { });

假設 listen() 在事件循環開始時運行,但是監聽回調放在 setImmediate() 中。 除非傳遞主機名,否則將立即綁定到端口。 要使事件循環繼續,它必須達到輪詢階段,這意味著可能已經接收到連接的非零概率允許在偵聽事件之前觸發連接事件。

另一個例子是運行一個函數構造函數,比如繼承自 EventEmitter ,它想在構造函數中調用一個事件:

const EventEmitter = require('events');const util = require('util');function MyEmitter() { EventEmitter.call(this); this.emit('event');}util.inherits(MyEmitter, EventEmitter);const myEmitter = new MyEmitter();myEmitter.on('event', () => { console.log('an event occurred!');});

您無法立即從構造函數中發出事件,因為腳本將不會處理到用戶為該事件分配回調的位置。 因此,在構造函數本身中,您可以使用 process.nextTick() 來設置回調以在構造函數完成后發出事件,從而提供預期的結果:

const EventEmitter = require('events');const util = require('util');function MyEmitter() { EventEmitter.call(this); // use nextTick to emit the event once a handler is assigned process.nextTick(() => { this.emit('event'); });}util.inherits(MyEmitter, EventEmitter);const myEmitter = new MyEmitter();myEmitter.on('event', () => { console.log('an event occurred!');});

總結

以上所述是小編給大家介紹的深入淺析Node.js 事件循環、定時器和process.nextTick() ,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對VeVb武林網網站的支持!


注:相關教程知識閱讀請移步到JavaScript/Ajax教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产精品1区2区在线观看| 亚洲韩国青草视频| 国内精品久久久久伊人av| 在线视频中文亚洲| 日韩电影网在线| 久久精品国产电影| 国产99视频在线观看| 欧美高清视频在线观看| 亚洲国产日韩精品在线| 91精品免费久久久久久久久| 亚洲国产精品va在线看黑人动漫| 日韩中文字幕在线免费观看| 亚洲夜晚福利在线观看| 国产精品久久久久久久天堂| 精品欧美激情精品一区| 国产精品入口福利| 久久成人18免费网站| 日韩欧美精品网址| 国产一区二中文字幕在线看| 精品欧美aⅴ在线网站| 国产精品无av码在线观看| 精品国产一区二区三区四区在线观看| 清纯唯美日韩制服另类| 国产精品高潮呻吟久久av黑人| 91精品中国老女人| 亚洲亚裔videos黑人hd| 亚洲精品国产精品久久清纯直播| 久久久久久12| 欧美日韩免费在线观看| 久久久精品2019中文字幕神马| 国产成人精品综合久久久| 高清日韩电视剧大全免费播放在线观看| 永久免费看mv网站入口亚洲| 一色桃子一区二区| 亚洲国产精品久久久久秋霞蜜臀| 欧美视频在线视频| 欧美一区深夜视频| 日韩av影片在线观看| 成人黄色短视频在线观看| 性色av一区二区三区在线观看| 久久久久久中文字幕| 国产欧美精品va在线观看| 欧美色视频日本版| 久久久日本电影| 久久色精品视频| 国模极品一区二区三区| 久久精品久久精品亚洲人| 久久亚洲精品一区二区| 日韩精品在线影院| 黑人巨大精品欧美一区二区一视频| 国产精品露脸av在线| 欧美高清理论片| 91成品人片a无限观看| 亚洲色图色老头| 疯狂做受xxxx高潮欧美日本| 日韩中文字幕在线观看| 18性欧美xxxⅹ性满足| 国产精自产拍久久久久久蜜| 日韩成人中文字幕在线观看| 亚洲男人天堂视频| 91免费在线视频网站| 国产中文日韩欧美| www欧美日韩| 精品国产鲁一鲁一区二区张丽| 日韩av手机在线| 伊人伊成久久人综合网站| 911国产网站尤物在线观看| 97超级碰碰人国产在线观看| 欧美中文字幕视频在线观看| 国产午夜精品麻豆| 国产一区二区三区高清在线观看| 欧美激情亚洲一区| 日本高清视频一区| 欧美国产在线电影| 性色av一区二区三区| 国产精品久久久久福利| 欧美日韩色婷婷| 不卡av在线播放| 欧美肥婆姓交大片| 欧美怡红院视频一区二区三区| 成人有码在线播放| 亚洲精品av在线播放| 成人免费网站在线观看| 久久露脸国产精品| 国产精品久久久久久久久久免费| 国产亚洲精品va在线观看| 在线成人中文字幕| 亚洲欧美日本伦理| 欧美视频第一页| 久久久久久69| 亚洲电影免费观看高清完整版在线| 中文字幕亚洲欧美日韩2019| 91成人免费观看网站| 日本19禁啪啪免费观看www| 91国产高清在线| 青青草原一区二区| 亚洲男人天堂久| 日韩免费av一区二区| 91精品久久久久久久久| 色综合天天综合网国产成人网| 国产亚洲一区二区在线| 欧美精品999| 国产精品国模在线| 久久久亚洲欧洲日产国码aⅴ| 国产精品久久久久久亚洲调教| 欧美性猛交xxxx乱大交极品| 在线播放国产一区二区三区| 欧美一区在线直播| 亚洲欧美日韩高清| 欧美日韩激情视频| 日本高清视频一区| 亚洲国产婷婷香蕉久久久久久| 亚洲激情中文字幕| 国产成人av在线| 精品视频—区二区三区免费| 亚洲色图校园春色| 亚洲深夜福利视频| 久久成人人人人精品欧| 夜夜嗨av一区二区三区四区| 久久精品视频免费播放| 在线亚洲欧美视频| 国产精品高清在线| 91精品在线一区| 欧美做爰性生交视频| 国产成人精品久久久| 久久精品视频在线| 国产在线精品播放| 久久成年人免费电影| 久久久免费av| 91精品综合久久久久久五月天| 中文字幕日韩精品有码视频| 欧美超级乱淫片喷水| 亚洲人成电影网站色xx| 日本成人免费在线| 亚洲精品视频网上网址在线观看| 久久艳片www.17c.com| 久热精品视频在线观看| 国产欧美一区二区三区在线| 久久精品国产欧美亚洲人人爽| 欧美xxxx14xxxxx性爽| 日韩av在线最新| 亚洲电影免费观看高清完整版| 欧美日韩另类在线| 亚洲人成电影在线播放| 欧美精品在线第一页| 国产精品揄拍一区二区| 久久国产精品网站| 亚洲人成电影在线播放| 亚洲一区二区三区在线视频| 欧美一区二区三区四区在线| 九色成人免费视频| 欧美日韩中文字幕在线视频| 欧美午夜www高清视频| 日韩电影大全免费观看2023年上| 亚洲天天在线日亚洲洲精| 欧美劲爆第一页| 成人免费观看49www在线观看| 97视频人免费观看| 日韩在线观看免费全| 97涩涩爰在线观看亚洲| 久久久久久久久91| 97超级碰在线看视频免费在线看| 国产精品99蜜臀久久不卡二区| 91精品国产综合久久香蕉最新版|