内容简介:nodejs 具有事件驱动和非阻塞但线程的特点,使相关应用变得比较轻量和高效。当应用程序需要相关I/O操作时,线程并不会阻塞,而是把I/O操作移交给底层类库(如:libuv)。此时nodejs线程会去处理其他的任务,当底层库处理完相关的I/O操作后,会将主动权再次交还给nodejs线程。因此event loop的作用就是起到调度线程的作用,如当底层类库处理I/O操作后调度nodejs单线程处理后续的工作。也就是说当nodejs 程序启动的时候,它会开启一个event loop以实现异步的api调度、sch
nodejs 具有事件驱动和非阻塞但线程的特点,使相关应用变得比较轻量和高效。当应用程序需要相关I/O操作时,线程并不会阻塞,而是把I/O操作移交给底层类库(如:libuv)。此时nodejs线程会去处理其他的任务,当底层库处理完相关的I/O操作后,会将主动权再次交还给nodejs线程。因此event loop的作用就是起到调度线程的作用,如当底层类库处理I/O操作后调度nodejs单线程处理后续的工作。也就是说当nodejs 程序启动的时候,它会开启一个event loop以实现异步的api调度、schedule timers 、回调process.nextTick()。
从上也可以看出nodejs 虽说是单线程,但是在底层类库处理异步操作的时候仍然是多线程。
2.引出问题
在node环境中我们运行如下代码,会出现怎么样的执行结果?
let fs = require('fs'); setTimeout(function(){ Promise.resolve().then(()=>{ console.log('then2'); }) },0); Promise.resolve().then(()=>{ console.log('then1'); }); fs.readFile('./gitigore',function(){ process.nextTick(function(){ console.log('nextTick') }) setImmediate(()=>{ console.log('setImmediate') }); }); 复制代码
在node环境的执行结果是
then1 then2 nextTick setImmediate 复制代码
3.开始事件循环之前,nodejs初始化
产出这样的结果,来源于Node.js对事件的循环操作顺序。在Node.js的官方文档中,对初始化event loop有这样的描述 The Node.js Event Loop, Timers, and process.nextTick()
-当Node.js启动的时候,他会初始化Event Loop,处理提供的输入脚本,这可能会使异步API调用,调用timers,或调用process.nextTick,然后开始处理事件循环,下面是一个经典的事件循环操作顺序
┌───────────────────────────────────┐ ┌─>│timers(计时器)执行 │ │ |setTimeout以及setInterval的回调 │ │ └──────────┬────────────────────────┘ │ ┌──────────┴────────────┐ │ │ I/O callbacks │ │ 处理网络,流,TCP的错误 │ │ │ callback │ │ └──────────┬────────────┘ │ ┌──────────┴────────────┐ │ │ idle, prepare │ │ │ node内部使用 │ │ └──────────┬────────────┘ ┌───────────────┐ │ ┌──────────┴────────────┐ │ incoming: │ │ │poll(轮询) │<─────┤ connections, │ │ │ 执行poll中的i/o队列检查 │ │data, etc. │ │ │定时器是否到时 │ └───────────────┘ │ └──────────┬────────────┘ │ ┌──────────┴────────────┐ │ │ check │ │ │ 存放setImmediate回调 │ │ └──────────┬────────────┘ │ ┌──────────┴────────────┐ └──┤ close callbacks │ │ 关闭的回调例如 │ │ socket.on('close') │ └───────────────────────┘ 复制代码
其中,每个盒子都是 Event Loop的一个阶段,当Event Loop进入到某个阶段的时候,就会将该阶段队列里的回调拿出来执行,直到队列为空。
几个队列
Timers Queue - 计时器队列 I/O Queue - 输入输出队列 Check Queue - 检查队列 Close Queue - guangbi 队列 复制代码
除了上面循环阶段的任务类型,还有浏览器和nodejs共有的微任务(micro task)和node的 process.nextTick。分别称其对应的队列为MircoTask Queue和NextTick Queue
4. 开始循环之后:
依据上述6个阶段依次执行,每次拿出当前阶段的全部任务执行,清空NextTick队列,清空微任务队列,再执行下一阶段,全部6个阶段完毕后,进入下一轮的循环。
即用一张图表述为
- 结合代码
let fs = require('fs'); setTimeout(function(){ Promise.resolve().then(()=>{ console.log('then2'); }) },0); Promise.resolve().then(()=>{ console.log('then1'); }); fs.readFile('./gitigore',function(){ process.nextTick(function(){ console.log('nextTick') }) setImmediate(()=>{ console.log('setImmediate') }); }); 复制代码
回看我们开头展示的代码,这里我们的队列中显然包含有
setTimeout Promise.resolve().then fs.readFile 复制代码
这样的三个主要的任务队列 依据循环阶段,我们将代码按照循环阶段的顺序展示和执行
// 清空TimerQueue setTimeout(...) // 清空该进程中的微任务 // then1位置的Promise先进入任务队列 Promise.resolve().then(()=>{ console.log('then1'); // then1 }); Promise.resolve().then(()=>{ console.log('then2'); // then2 }) // 接着进入IO队列 fs.readFile(...) // 优先清空IO队列的NextTick Queue process.nextTick(function(){ console.log('nextTick') // nextTick }) // 清空micro queue setImmediate(()=>{ console.log('setImmediate')//setImmediate }); 复制代码
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 详解JS事件 - 事件模型/事件流/事件代理/事件对象/自定义事件
- React 事件和 Dom 事件
- react事件系统之事件触发
- JS 中的自定义事件和模拟事件
- Flex 事件分发(FlexViewer事件机制)剥离过程
- 事件社交网络:深度用户模型的内容事件推荐
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Base64 编码/解码
Base64 编码/解码
XML 在线格式化
在线 XML 格式化压缩工具