真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Node.js中的定時器是什么

小編給大家分享一下Node.js中的定時器是什么,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

創(chuàng)新互聯(lián)長期為數(shù)千家客戶提供的網(wǎng)站建設(shè)服務(wù),團隊從業(yè)經(jīng)驗10年,關(guān)注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為廬江企業(yè)提供專業(yè)的成都網(wǎng)站建設(shè)、成都網(wǎng)站設(shè)計廬江網(wǎng)站改版等技術(shù)服務(wù)。擁有10年豐富建站經(jīng)驗和眾多成功案例,為您定制開發(fā)。

timer 用于安排函數(shù)在未來某個時間點被調(diào)用,Node.js 中的定時器函數(shù)實現(xiàn)了與 Web 瀏覽器提供的定時器 API 類似的 API,但是使用了事件循環(huán)實現(xiàn),Node.js 中有四個相關(guān)的方法

  • setTimeout(callback, delay[, ...args])

  • setInterval(callback[, ...args])

  • setImmediate(callback[, ...args])

  • process.nextTick(callback[, ...args])

前兩個含義和 web 上的是一致的,后兩個是 Node.js 獨有的,效果看起來就是 setTimeout(callback, 0),在 Node.js 編程中使用的最多

Node.js 不保證回調(diào)被觸發(fā)的確切時間,也不保證它們的順序,回調(diào)會在盡可能接近指定的時間被調(diào)用。setTimeout 當(dāng) delay 大于 2147483647 或小于 1 時,則 delay 將會被設(shè)置為 1, 非整數(shù)的 delay 會被截斷為整數(shù)

奇怪的執(zhí)行順序

看一個示例,用幾種方法分別異步打印一個數(shù)字

setImmediate(console.log, 1);
setTimeout(console.log, 1, 2);
Promise.resolve(3).then(console.log);
process.nextTick(console.log, 4);
console.log(5);

會打印 5 4 3 2 1 或者 5 4 3 1 2

同步 & 異步

第五行是同步執(zhí)行,其它都是異步的

setImmediate(console.log, 1);
setTimeout(console.log, 1, 2);
Promise.resolve(3).then(console.log);
process.nextTick(console.log, 4);
/****************** 同步任務(wù)和異步任務(wù)的分割線 ********************/
console.log(5);

所以先打印 5,這個很好理解,剩下的都是異步操作,Node.js 按照什么順序執(zhí)行呢?

event loop

Node.js 啟動后會初始化事件輪詢,過程中可能處理異步調(diào)用、定時器調(diào)度和 process.nextTick(),然后開始處理event loop。官網(wǎng)中有這樣一張圖用來介紹 event loop 操作順序

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

event loop 的每個階段都有一個任務(wù)隊列,當(dāng) event loop 進入給定的階段時,將執(zhí)行該階段的任務(wù)隊列,直到隊列清空或執(zhí)行的回調(diào)達到系統(tǒng)上限后,才會轉(zhuǎn)入下一個階段,當(dāng)所有階段被順序執(zhí)行一次后,稱 event loop 完成了一個 tick

異步操作都被放到了下一個 event loop tick 中,process.nextTick 在進入下一次 event loop tick 之前執(zhí)行,所以肯定在其它異步操作之前

setImmediate(console.log, 1);
setTimeout(console.log, 1, 2);
Promise.resolve(3).then(console.log);
/****************** 下次 event loop tick 分割線 ********************/
process.nextTick(console.log, 4);
/****************** 同步任務(wù)和異步任務(wù)的分割線 ********************/
console.log(5);

各個階段主要任務(wù)

  • timers:執(zhí)行 setTimeout、setInterval 回調(diào)

  • pending callbacks:執(zhí)行 I/O(文件、網(wǎng)絡(luò)等) 回調(diào)

  • idle, prepare:僅供系統(tǒng)內(nèi)部調(diào)用

  • poll:獲取新的 I/O 事件,執(zhí)行相關(guān)回調(diào),在適當(dāng)條件下把阻塞 node

  • check:setImmediate 回調(diào)在此階段執(zhí)行

  • close callbacks:執(zhí)行 socket 等的 close 事件回調(diào)

日常開發(fā)中絕大部分異步任務(wù)都是在 timers、poll、check 階段處理的

timers

Node.js 會在 timers 階段檢查是否有過期的 timer,如果存在則把回調(diào)放到 timer 隊列中等待執(zhí)行,Node.js 使用單線程,受限于主線程空閑情況和機器其它進程影響,并不能保證 timer 按照精確時間執(zhí)行
定時器主要有兩種

  • Immediate

  • Timeout

Immediate 類型的計時器回調(diào)會在 check階段被調(diào)用,Timeout 計時器會在設(shè)定的時間過期后盡快的調(diào)用回調(diào),但

setTimeout(() => {
  console.log('timeout');
}, 0);

setImmediate(() => {
  console.log('immediate');
});

多次執(zhí)行會發(fā)現(xiàn)打印的順序不一樣

poll

poll 階段主要有兩個任務(wù)

  • 計算應(yīng)該阻塞和輪詢 I/O 的時間

  • 然后,處理 poll 隊列里的事件

當(dāng)event loop進入 poll 階段且沒有被調(diào)度的計時器時

  • 如果 poll 隊列不是空的 ,event loop 將循環(huán)訪問回調(diào)隊列并同步執(zhí)行,直到隊列已用盡或者達到了系統(tǒng)或達到最大回調(diào)數(shù)
  • 如果 poll 隊列是空的
    • 如果有 setImmediate() 任務(wù),event loop 會在結(jié)束 poll階段后進入 check階段
    • 如果沒有 setImmediate()任務(wù),event loop 阻塞在 poll階段等待回調(diào)被添加到隊列中,然后立即執(zhí)行

一旦 poll隊列為空,event loop 將檢查 timer 隊列是否為空,如果非空則進入下一輪 event loop

上面提到了如果在不同的 I/O 里,不能確定 setTimeout 和 setImmediate 的執(zhí)行順序,但如果 setTimeout 和 setImmediate 在一個 I/O 回調(diào)里,肯定是 setImmediate 先執(zhí)行,因為在 poll 階段檢查到有 setImmediate() 任務(wù),event loop 直接進入 check 階段執(zhí)行 setImmediate 回調(diào)

const fs = require('fs');
fs.readFile(__filename, () => {
  setTimeout(() => {
    console.log('timeout');
  }, 0);
  setImmediate(() => {
    console.log('immediate');
  });
});

check

在該階段執(zhí)行 setImmediate 回調(diào)

為什么 Promise.then 比 setTimeout 早一些

前端同學(xué)肯定都聽說過 micoTask 和 macroTask,Promise.then 屬于 microTask,在瀏覽器環(huán)境下 microTask 任務(wù)會在每個 macroTask 執(zhí)行最末端調(diào)用

在 Node.js 環(huán)境下 microTask 會在每個階段完成之間調(diào)用,也就是每個階段執(zhí)行最后都會執(zhí)行一下 microTask 隊列

setImmediate(console.log, 1);
setTimeout(console.log, 1, 2);
/****************** microTask 分割線 ********************/
Promise.resolve(3).then(console.log); // microTask 分割線
/****************** 下次 event loop tick 分割線 ********************/
process.nextTick(console.log, 4);
/****************** 同步任務(wù)和異步任務(wù)的分割線 ********************/
console.log(5);

setImmediate VS process.nextTick

setImmediate 聽起來是立即執(zhí)行,process.nextTick 聽起來是下一個時鐘執(zhí)行,為什么效果是反過來的?這就要從那段不堪回首的歷史講起

最開始的時候只有 process.nextTick 方法,沒有 setImmediate 方法,通過上面的分析可以看出來任何時候調(diào)用 process.nextTick(),nextTick 會在 event loop 之前執(zhí)行,直到 nextTick 隊列被清空才會進入到下一 event loop,如果出現(xiàn) process.nextTick 的遞歸調(diào)用程序沒有被正確結(jié)束,那么 IO 的回調(diào)將沒有機會被執(zhí)行

const fs = require('fs');

fs.readFile('a.txt', (err, data) => {
	console.log('read file task done!');
});

let i = 0;
function test(){
	if(i++ < 999999) {
  	console.log(`process.nextTick ${i}`);
    process.nextTick(test);
  }
}
test();

執(zhí)行程序?qū)⒎祷?/p>

nextTick 1
nextTick 2
...
...
nextTick 999999
read file task done!

于是乎需要一個不這么 bug 的調(diào)用,setImmediate 方法出現(xiàn)了,比較令人費解的是在 process.nextTick 起錯名字的情況下,setImmediate 也用了一個錯誤的名字以示區(qū)分。。。

那么是不是編程中應(yīng)該杜絕使用  process.nextTick 呢?官方推薦大部分時候應(yīng)該使用 setImmediate,同時對 process.nextTick 的最大調(diào)用堆棧做了限制,但 process.nextTick 的調(diào)用機制確實也能為我們解決一些棘手的問題

  • 允許用戶在 even tloop 開始之前 處理異常、執(zhí)行清理任務(wù)

  • 允許回調(diào)在調(diào)用棧 unwind 之后,下次 event loop 開始之前執(zhí)行

一個類繼承了 EventEmitter,而且想在實例化的時候觸發(fā)一個事件

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!');
});

在構(gòu)造函數(shù)執(zhí)行 this.emit('event') 會導(dǎo)致事件觸發(fā)比事件回調(diào)函數(shù)綁定早,使用 process.nextTick 可以輕松實現(xiàn)預(yù)期效果

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中的定時器是什么”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!


當(dāng)前文章:Node.js中的定時器是什么
分享地址:http://weahome.cn/article/gehgpe.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部