NodeJs 的 Event loop 事件循环机制详解 |
您所在的位置:网站首页 › js事件循环概述 › NodeJs 的 Event loop 事件循环机制详解 |
什么是事件轮询
事件循环是 Node.js 处理非阻塞 I/O 操作的机制——尽管 JavaScript 是单线程处理的——当有可能的时候,它们会把操作转移到系统内核中去。 下面的图表显示了事件循环的概述以及操作顺序。 ┌───────────────────────────┐ ┌─>│ timers │ │ └─────────────┬─────────────┘ │ ┌─────────────┴─────────────┐ │ │ IO / callbacks │ │ └─────────────┬─────────────┘ │ ┌─────────────┴─────────────┐ │ │ idle, prepare │ │ └─────────────┬─────────────┘ ┌───────────────┐ │ ┌─────────────┴─────────────┐ │ incoming: │ │ │ poll │ { console.log('timeout'); }, 0); setImmediate(() => { console.log('immediate'); }); $ node timeout_vs_immediate.js timeout immediate $ node timeout_vs_immediate.js immediate timeout但是,如果你把这两个函数放入一个 I/O 循环内调用,setImmediate 总是被优先调用: // timeout_vs_immediate.js const fs = require('fs'); fs.readFile(__filename, () => { setTimeout(() => { console.log('timeout'); }, 0); setImmediate(() => { console.log('immediate'); }); }); $ node timeout_vs_immediate.js immediate timeout $ node timeout_vs_immediate.js immediate timeout使用 setImmediate() 超过 setTimeout() 的主要优点是 setImmediate() 在任何计时器(如果在 I/O 周期内)都将始终执行,而不依赖于存在多少个计时器。 process.nextTick()process.nextTick() 在技术上不是事件循环的一部分,无论事件循环的当前阶段如何,都将在当前操作完成后处理 nextTickQueue。这里的一个操作被视作为一个从 C++ 底层处理开始过渡,并且处理需要执行的 JavaScript 代码。 回顾我们的关系图,任何时候在给定的阶段中调用 process.nextTick(),所有传递到 process.nextTick() 的回调将在事件循环继续之前得到解决。这可能会造成一些糟糕的情况, 因为它允许您通过进行递归 process.nextTick() 来“饿死”您的 I/O 调用,阻止事件循环到达 轮询 阶段。 一个题目 // test.js process.nextTick(function() { console.log('next tick'); }); setTimeout(function() { console.log('settimeout'); }); (async function() { console.log('async promise'); })(); setImmediate(function() { console.log('setimmediate'); }); $ node test.js async promise next tick settimeout setimmediate 没有await,async那句其实是同步执行的,故而第一句输出。 next tick 在任何事件循环阶段继续之前得到解决,故而第二句 setTimeout 在主线程中与 setImmediate 的执行顺序是非确定性的 // test.js setTimeout(function () { process.nextTick(function() { console.log('next tick'); }); setTimeout(function() { console.log('settimeout'); }); (async function() { console.log('async promise'); })(); setImmediate(function() { console.log('setimmediate'); }); }) $ node test.js async promise next tick setimmediate settimeoutsetimmediate 与 settimeout 放入一个 I/O 循环内调用,则 setImmediate 总是被优先调用 node >= 11 ? setTimeout(()=>{ console.log('timer1') setImmediate(function () { console.log('immd 1'); }) Promise.resolve().then(function() { console.log('promise1') }) }, 0) setTimeout(()=>{ console.log('timer2') setImmediate(function () { console.log('immd 2'); }) Promise.resolve().then(function() { console.log('promise2') }) }, 0)在 node 11 及以上版本打印得 timer1 promise1 timer2 promise2 immd 1 immd 2在 node 版本为 8.11.2 打印 timer1 timer2 promise1 promise2 immd 1 immd 2这是因为 < 11 得版本中 若第一个定时器任务出队并执行完,发现队首的任务仍然是一个定时器,那么就将微任务暂时保存,直接去执行新的定时器任务,当新的定时器任务执行完后,再一一执行中途产生的微任务。 nodejs 和 浏览器关于eventLoop的主要区别两者最主要的区别在于浏览器中的微任务是在每个相应的宏任务中执行的,而nodejs中的微任务是在不同阶段之间执行的。 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |