标签:通过 浏览器 connect eve uid 回调 五行 occurred mic
极简 Node.js 入门系列教程:https://www.yuque.com/sunluyong/node
timer 用于安排函数在未来某个时间点被调用,Node.js 中的定时器函数实现了与 Web 浏览器提供的定时器 API 类似的 API,但是使用了事件循环实现,Node.js 中有四个相关的方法
前两个含义和 web 上的是一致的,后两个是 Node.js 独有的,效果看起来就是 setTimeout(callback, 0),在 Node.js 编程中使用的最多
Node.js 不保证回调被触发的确切时间,也不保证它们的顺序,回调会在尽可能接近指定的时间被调用。setTimeout?当 delay 大于 2147483647 或小于 1 时,则 delay 将会被设置为 1, 非整数的 delay 会被截断为整数
看一个示例,用几种方法分别异步打印一个数字
setImmediate(console.log, 1);
setTimeout(console.log, 1, 2);
Promise.resolve(3).then(console.log);
process.nextTick(console.log, 4);
console.log(5);
第五行是同步执行,其它都是异步的
setImmediate(console.log, 1);
setTimeout(console.log, 1, 2);
Promise.resolve(3).then(console.log);
process.nextTick(console.log, 4);
/****************** 同步任务和异步任务的分割线 ********************/
console.log(5);
所以先打印 5,这个很好理解,剩下的都是异步操作,Node.js 按照什么顺序执行呢?
Node.js 启动后会初始化事件轮询,过程中可能处理异步调用、定时器调度和 process.nextTick(),然后开始处理event loop。官网中有这样一张图用来介绍?event loop 操作顺序
┌───────────────────────────┐
┌─>│ timers │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ pending callbacks │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ idle, prepare │
│ └─────────────┬─────────────┘ ┌───────────────┐
│ ┌─────────────┴─────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └─────────────┬─────────────┘ │ data, etc. │
│ ┌─────────────┴─────────────┐ └───────────────┘
│ │ check │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
└──┤ close callbacks │
└───────────────────────────┘
event loop 的每个阶段都有一个任务队列,当 event loop 进入给定的阶段时,将执行该阶段的任务队列,直到队列清空或执行的回调达到系统上限后,才会转入下一个阶段,当所有阶段被顺序执行一次后,称 event loop 完成了一个 tick
异步操作都被放到了下一个 event loop tick 中,process.nextTick 在进入下一次 event loop tick 之前执行,所以肯定在其它异步操作之前
setImmediate(console.log, 1);
setTimeout(console.log, 1, 2);
Promise.resolve(3).then(console.log);
/****************** 下次 event loop tick 分割线 ********************/
process.nextTick(console.log, 4);
/****************** 同步任务和异步任务的分割线 ********************/
console.log(5);
各个阶段主要任务
日常开发中绝大部分异步任务都是在 timers、poll、check 阶段处理的
Node.js 会在 timers 阶段检查是否有过期的 timer,如果存在则把回调放到 timer 队列中等待执行,Node.js 使用单线程,受限于主线程空闲情况和机器其它进程影响,并不能保证 timer 按照精确时间执行
定时器主要有两种
Immediate 类型的计时器回调会在 check 阶段被调用,Timeout 计时器会在设定的时间过期后尽快的调用回调,但
setTimeout(() => {
console.log(‘timeout‘);
}, 0);
setImmediate(() => {
console.log(‘immediate‘);
});
poll 阶段主要有两个任务
当event loop进入 poll 阶段且没有被调度的计时器时
一旦 poll 队列为空,event loop 将检查 timer 队列是否为空,如果非空则进入下一轮 event loop
上面提到了如果在不同的 I/O 里,不能确定?setTimeout 和 setImmediate 的执行顺序,但如果 setTimeout 和 setImmediate 在一个 I/O 回调里,肯定是 setImmediate 先执行,因为在 poll 阶段检查到有?setImmediate() 任务,event loop 直接进入 check 阶段执行?setImmediate 回调
const fs = require(‘fs‘);
fs.readFile(__filename, () => {
setTimeout(() => {
console.log(‘timeout‘);
}, 0);
setImmediate(() => {
console.log(‘immediate‘);
});
});
在该阶段执行 setImmediate 回调
前端同学肯定都听说过 micoTask 和 macroTask,Promise.then 属于 microTask,在浏览器环境下 microTask 任务会在每个 macroTask 执行最末端调用
在 Node.js 环境下 microTask 会在每个阶段完成之间调用,也就是每个阶段执行最后都会执行一下 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);
/****************** 同步任务和异步任务的分割线 ********************/
console.log(5);
setImmediate 听起来是立即执行,process.nextTick 听起来是下一个时钟执行,为什么效果是反过来的?这就要从那段不堪回首的历史讲起
最开始的时候只有 process.nextTick 方法,没有 setImmediate 方法,通过上面的分析可以看出来任何时候调用?process.nextTick(),nextTick 会在 event loop 之前执行,直到 nextTick 队列被清空才会进入到下一 event loop,如果出现 process.nextTick 的递归调用程序没有被正确结束,那么 IO 的回调将没有机会被执行
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();
执行程序将返回
nextTick 1
nextTick 2
...
...
nextTick 999999
read file task done!
于是乎需要一个不这么 bug 的调用,setImmediate 方法出现了,比较令人费解的是在 process.nextTick 起错名字的情况下,setImmediate 也用了一个错误的名字以示区分。。。
那么是不是编程中应该杜绝使用? process.nextTick 呢?官方推荐大部分时候应该使用?setImmediate,同时对 process.nextTick 的最大调用堆栈做了限制,但 process.nextTick 的调用机制确实也能为我们解决一些棘手的问题
一个类继承了 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!‘);
});
在构造函数执行 this.emit(‘event‘)
?会导致事件触发比事件回调函数绑定早,使用 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!‘);
});
标签:通过 浏览器 connect eve uid 回调 五行 occurred mic
原文地址:https://www.cnblogs.com/dolphinX/p/13494437.html