最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解Node 定時(shí)器

 更新時(shí)間:2018年02月26日 09:07:28   作者:阮一峰  
這篇文章主要介紹了Node 定時(shí)器的相關(guān)知識(shí),非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下

JavaScript 是單線程運(yùn)行,異步操作特別重要。

只要用到引擎之外的功能,就需要跟外部交互,從而形成異步操作。由于異步操作實(shí)在太多,JavaScript 不得不提供很多異步語(yǔ)法。這就好比,有些人老是受打擊, 他的抗打擊能力必須變得很強(qiáng),否則他就完蛋了。

Node 的異步語(yǔ)法比瀏覽器更復(fù)雜,因?yàn)樗梢愿鷥?nèi)核對(duì)話,不得不搞了一個(gè)專門的庫(kù) libuv 做這件事。這個(gè)庫(kù)負(fù)責(zé)各種回調(diào)函數(shù)的執(zhí)行時(shí)間,畢竟異步任務(wù)最后還是要回到主線程,一個(gè)個(gè)排隊(duì)執(zhí)行。

為了協(xié)調(diào)異步任務(wù),Node 居然提供了四個(gè)定時(shí)器,讓任務(wù)可以在指定的時(shí)間運(yùn)行。

  • setTimeout()
  • setInterval()
  • setImmediate()
  • process.nextTick()

前兩個(gè)是語(yǔ)言的標(biāo)準(zhǔn),后兩個(gè)是 Node 獨(dú)有的。它們的寫法差不多,作用也差不多,不太容易區(qū)別。

你能說(shuō)出下面代碼的運(yùn)行結(jié)果嗎?

// test.js
setTimeout(() => console.log(1));
setImmediate(() => console.log(2));
process.nextTick(() => console.log(3));
Promise.resolve().then(() => console.log(4));
(() => console.log(5))();

運(yùn)行結(jié)果如下。

$ node test.js

如果你能一口說(shuō)對(duì),可能就不需要再看下去了。本文詳細(xì)解釋,Node 怎么處理各種定時(shí)器,或者更廣義地說(shuō),libuv 庫(kù)怎么安排異步任務(wù)在主線程上執(zhí)行。

一、同步任務(wù)和異步任務(wù)

首先,同步任務(wù)總是比異步任務(wù)更早執(zhí)行。

前面的那段代碼,只有最后一行是同步任務(wù),因此最早執(zhí)行。

(() => console.log(5))();

二、本輪循環(huán)和次輪循環(huán)

異步任務(wù)可以分成兩種。

追加在本輪循環(huán)的異步任務(wù)
追加在次輪循環(huán)的異步任務(wù)

所謂”循環(huán)”,指的是事件循環(huán)(event loop)。這是 JavaScript 引擎處理異步任務(wù)的方式,后文會(huì)詳細(xì)解釋。這里只要理解,本輪循環(huán)一定早于次輪循環(huán)執(zhí)行即可。

Node 規(guī)定,process.nextTick和Promise的回調(diào)函數(shù),追加在本輪循環(huán),即同步任務(wù)一旦執(zhí)行完成,就開(kāi)始執(zhí)行它們。而setTimeout、setInterval、setImmediate的回調(diào)函數(shù),追加在次輪循環(huán)。

這就是說(shuō),文首那段代碼的第三行和第四行,一定比第一行和第二行更早執(zhí)行。

// 下面兩行,次輪循環(huán)執(zhí)行
setTimeout(() => console.log(1));
setImmediate(() => console.log(2));
// 下面兩行,本輪循環(huán)執(zhí)行
process.nextTick(() => console.log(3));
Promise.resolve().then(() => console.log(4));

三、process.nextTick()

process.nextTick這個(gè)名字有點(diǎn)誤導(dǎo),它是在本輪循環(huán)執(zhí)行的,而且是所有異步任務(wù)里面最快執(zhí)行的。

Node 執(zhí)行完所有同步任務(wù),接下來(lái)就會(huì)執(zhí)行process.nextTick的任務(wù)隊(duì)列。所以,下面這行代碼是第二個(gè)輸出結(jié)果。

process.nextTick(() => console.log(3));

基本上,如果你希望異步任務(wù)盡可能快地執(zhí)行,那就使用process.nextTick。

四、微任務(wù)

根據(jù)語(yǔ)言規(guī)格,Promise對(duì)象的回調(diào)函數(shù),會(huì)進(jìn)入異步任務(wù)里面的”微任務(wù)”(microtask)隊(duì)列。

微任務(wù)隊(duì)列追加在process.nextTick隊(duì)列的后面,也屬于本輪循環(huán)。所以,下面的代碼總是先輸出3,再輸出4。

process.nextTick(() => console.log(3));
Promise.resolve().then(() => console.log(4));
// 3
// 4

注意,只有前一個(gè)隊(duì)列全部清空以后,才會(huì)執(zhí)行下一個(gè)隊(duì)列。

process.nextTick(() => console.log(1));
Promise.resolve().then(() => console.log(2));
process.nextTick(() => console.log(3));
Promise.resolve().then(() => console.log(4));
// 1
// 3
// 2
// 4

上面代碼中,全部process.nextTick的回調(diào)函數(shù),執(zhí)行都會(huì)早于Promise的。

至此,本輪循環(huán)的執(zhí)行順序就講完了。

同步任務(wù)
process.nextTick()
微任務(wù)

五、事件循環(huán)的概念

下面開(kāi)始介紹次輪循環(huán)的執(zhí)行順序,這就必須理解什么是事件循環(huán)(event loop)了。

Node 的官方文檔是這樣介紹的。

“When Node.js starts, it initializes the event loop, processes the provided input script which may make async API calls, schedule timers, or call process.nextTick(), then begins processing the event loop.”

這段話很重要,需要仔細(xì)讀。它表達(dá)了三層意思。

首先,有些人以為,除了主線程,還存在一個(gè)單獨(dú)的事件循環(huán)線程。不是這樣的,只有一個(gè)主線程,事件循環(huán)是在主線程上完成的。

其次,Node 開(kāi)始執(zhí)行腳本時(shí),會(huì)先進(jìn)行事件循環(huán)的初始化,但是這時(shí)事件循環(huán)還沒(méi)有開(kāi)始,會(huì)先完成下面的事情。

  • 同步任務(wù)
  • 發(fā)出異步請(qǐng)求
  • 規(guī)劃定時(shí)器生效的時(shí)間

執(zhí)行process.nextTick()等等

最后,上面這些事情都干完了,事件循環(huán)就正式開(kāi)始了。

六、事件循環(huán)的六個(gè)階段

事件循環(huán)會(huì)無(wú)限次地執(zhí)行,一輪又一輪。只有異步任務(wù)的回調(diào)函數(shù)隊(duì)列清空了,才會(huì)停止執(zhí)行。

每一輪的事件循環(huán),分成六個(gè)階段。這些階段會(huì)依次執(zhí)行。

timers
I/O callbacks
idle, prepare
poll
check
close callbacks

每個(gè)階段都有一個(gè)先進(jìn)先出的回調(diào)函數(shù)隊(duì)列。只有一個(gè)階段的回調(diào)函數(shù)隊(duì)列清空了,該執(zhí)行的回調(diào)函數(shù)都執(zhí)行了,事件循環(huán)才會(huì)進(jìn)入下一個(gè)階段。

下面簡(jiǎn)單介紹一下每個(gè)階段的含義,詳細(xì)介紹可以看官方文檔,也可以參考 libuv 的源碼解讀。

(1)timers

這個(gè)是定時(shí)器階段,處理setTimeout()和setInterval()的回調(diào)函數(shù)。進(jìn)入這個(gè)階段后,主線程會(huì)檢查一下當(dāng)前時(shí)間,是否滿足定時(shí)器的條件。如果滿足就執(zhí)行回調(diào)函數(shù),否則就離開(kāi)這個(gè)階段。

(2)I/O callbacks

除了以下操作的回調(diào)函數(shù),其他的回調(diào)函數(shù)都在這個(gè)階段執(zhí)行。

  • setTimeout()和setInterval()的回調(diào)函數(shù)
  • setImmediate()的回調(diào)函數(shù)
  • 用于關(guān)閉請(qǐng)求的回調(diào)函數(shù),比如socket.on('close', ...)

(3)idle, prepare

該階段只供 libuv 內(nèi)部調(diào)用,這里可以忽略。

(4)Poll

這個(gè)階段是輪詢時(shí)間,用于等待還未返回的 I/O 事件,比如服務(wù)器的回應(yīng)、用戶移動(dòng)鼠標(biāo)等等。

這個(gè)階段的時(shí)間會(huì)比較長(zhǎng)。如果沒(méi)有其他異步任務(wù)要處理(比如到期的定時(shí)器),會(huì)一直停留在這個(gè)階段,等待 I/O 請(qǐng)求返回結(jié)果。

(5)check

該階段執(zhí)行setImmediate()的回調(diào)函數(shù)。

(6)close callbacks

該階段執(zhí)行關(guān)閉請(qǐng)求的回調(diào)函數(shù),比如socket.on('close', ...)。

七、事件循環(huán)的示例

下面是來(lái)自官方文檔的一個(gè)示例。

const fs = require('fs');
const timeoutScheduled = Date.now();
// 異步任務(wù)一:100ms 后執(zhí)行的定時(shí)器
setTimeout(() => {
 const delay = Date.now() - timeoutScheduled;
 console.log(`${delay}ms`);
}, 100);
// 異步任務(wù)二:至少需要 200ms 的文件讀取
fs.readFile('test.js', () => {
 const startCallback = Date.now();
 while (Date.now() - startCallback < 200) {
 // 什么也不做
 }
});

上面代碼有兩個(gè)異步任務(wù),一個(gè)是 100ms 后執(zhí)行的定時(shí)器,一個(gè)是至少需要 200ms 的文件讀取。請(qǐng)問(wèn)運(yùn)行結(jié)果是什么?

腳本進(jìn)入第一輪事件循環(huán)以后,沒(méi)有到期的定時(shí)器,也沒(méi)有已經(jīng)可以執(zhí)行的 I/O 回調(diào)函數(shù),所以會(huì)進(jìn)入 Poll 階段,等待內(nèi)核返回文件讀取的結(jié)果。由于讀取小文件一般不會(huì)超過(guò) 100ms,所以在定時(shí)器到期之前,Poll 階段就會(huì)得到結(jié)果,因此就會(huì)繼續(xù)往下執(zhí)行。

第二輪事件循環(huán),依然沒(méi)有到期的定時(shí)器,但是已經(jīng)有了可以執(zhí)行的 I/O 回調(diào)函數(shù),所以會(huì)進(jìn)入 I/O callbacks 階段,執(zhí)行fs.readFile的回調(diào)函數(shù)。這個(gè)回調(diào)函數(shù)需要 200ms,也就是說(shuō),在它執(zhí)行到一半的時(shí)候,100ms 的定時(shí)器就會(huì)到期。但是,必須等到這個(gè)回調(diào)函數(shù)執(zhí)行完,才會(huì)離開(kāi)這個(gè)階段。

第三輪事件循環(huán),已經(jīng)有了到期的定時(shí)器,所以會(huì)在 timers 階段執(zhí)行定時(shí)器。最后輸出結(jié)果大概是200多毫秒。

八、setTimeout 和 setImmediate

由于setTimeout在 timers 階段執(zhí)行,而setImmediate在 check 階段執(zhí)行。所以,setTimeout會(huì)早于setImmediate完成。

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

上面代碼應(yīng)該先輸出1,再輸出2,但是實(shí)際執(zhí)行的時(shí)候,結(jié)果卻是不確定,有時(shí)還會(huì)先輸出2,再輸出1。

這是因?yàn)閟etTimeout的第二個(gè)參數(shù)默認(rèn)為0。但是實(shí)際上,Node 做不到0毫秒,最少也需要1毫秒,根據(jù)官方文檔,第二個(gè)參數(shù)的取值范圍在1毫秒到2147483647毫秒之間。也就是說(shuō),setTimeout(f, 0)等同于setTimeout(f, 1)。

實(shí)際執(zhí)行的時(shí)候,進(jìn)入事件循環(huán)以后,有可能到了1毫秒,也可能還沒(méi)到1毫秒,取決于系統(tǒng)當(dāng)時(shí)的狀況。如果沒(méi)到1毫秒,那么 timers 階段就會(huì)跳過(guò),進(jìn)入 check 階段,先執(zhí)行setImmediate的回調(diào)函數(shù)。

但是,下面的代碼一定是先輸出2,再輸出1。

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

上面代碼會(huì)先進(jìn)入 I/O callbacks 階段,然后是 check 階段,最后才是 timers 階段。因此,setImmediate才會(huì)早于setTimeout執(zhí)行。

總結(jié)

以上所述是小編給大家介紹的Node 定時(shí)器的相關(guān)知識(shí),希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!

相關(guān)文章

  • node.js利用mongoose獲取mongodb數(shù)據(jù)的格式化問(wèn)題詳解

    node.js利用mongoose獲取mongodb數(shù)據(jù)的格式化問(wèn)題詳解

    這篇文章主要給大家介紹了關(guān)于node.js利用mongoose獲取mongodb數(shù)據(jù)的格式化問(wèn)題,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)把。
    2017-10-10
  • Nodejs中的require函數(shù)的具體使用方法

    Nodejs中的require函數(shù)的具體使用方法

    這篇文章主要介紹了Nodejs中的require函數(shù)的具體使用方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2019-04-04
  • 詳解如何查看node端口被占用并殺死

    詳解如何查看node端口被占用并殺死

    這篇文章主要給大家介紹了如何查看node端口被占用并殺死,文中給出了相關(guān)的解決方法,并通過(guò)代碼示例給大家介紹的非常詳細(xì),對(duì)前端開(kāi)發(fā)要學(xué)會(huì)如何查看端口占用并殺死非常有用,需要的朋友可以參考下
    2024-01-01
  • 詳解如何讓Express支持async/await

    詳解如何讓Express支持async/await

    本篇文章主要介紹了詳解如何讓Express支持async/await,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-10-10
  • node+vue實(shí)現(xiàn)文件上傳功能

    node+vue實(shí)現(xiàn)文件上傳功能

    這篇文章主要介紹了node+vue實(shí)現(xiàn)文件上傳功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-05-05
  • koa2的中間件功能及應(yīng)用示例

    koa2的中間件功能及應(yīng)用示例

    這篇文章主要介紹了koa2的中間件功能及應(yīng)用示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-03-03
  • 初始Nodejs

    初始Nodejs

    本文屬于nodejs的基礎(chǔ)知識(shí)介紹篇,從nodejs的基本概念,到架構(gòu)示意圖,同步異步等方面做了詳細(xì)的解答,是篇非常不錯(cuò)的文章
    2014-11-11
  • 使用nodejs搭建微信小程序支付接口的詳細(xì)過(guò)程

    使用nodejs搭建微信小程序支付接口的詳細(xì)過(guò)程

    前段時(shí)間做微信支付,遇到了很多坑,網(wǎng)上也沒(méi)有講解的特別明白的,通過(guò)借鑒各路人才的經(jīng)驗(yàn),最后也完成了,下面這篇文章主要給大家介紹了關(guān)于使用nodejs搭建微信小程序支付接口的詳細(xì)過(guò)程,需要的朋友可以參考下
    2022-12-12
  • node.js文件的復(fù)制、創(chuàng)建文件夾等相關(guān)操作

    node.js文件的復(fù)制、創(chuàng)建文件夾等相關(guān)操作

    這篇文章主要給大家介紹了node.js文件的復(fù)制、創(chuàng)建文件夾等相關(guān)操作,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-02-02
  • nodejs中的http模塊與npm模塊使用

    nodejs中的http模塊與npm模塊使用

    這篇文章主要介紹了nodejs中的http模塊與npm模塊使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08

最新評(píng)論

祁东县| 夏津县| 屏南县| 遵义市| 米林县| 玛曲县| 海城市| 蓝田县| 玉树县| 汉中市| 青岛市| 和龙市| 三穗县| 洛隆县| 巴南区| 蕲春县| 山丹县| 吴旗县| 东源县| 铅山县| 呼和浩特市| 拜泉县| 莲花县| 磴口县| 堆龙德庆县| 漳浦县| 酉阳| 理塘县| 平舆县| 周宁县| 玛多县| 彭泽县| 通道| 鄄城县| 凤台县| 莱阳市| 安西县| 宁化县| 泸州市| 石河子市| 青铜峡市|