目录
1、process.nextTick() 介绍
2、setTimeout()
3、零延迟
4、setInterval()
5、递归setTimeout
6、setImmediate()
7、Node.js 事件派发器
1、process.nextTick() 介绍
Node.js中 process.nextTick函数以一种特殊的方式与事件循环交互。
当你试图理解Node.js事件循环时,它的一个重要部分是process.nextTick()。每次事件循环进行一次完整的行程,我们都称之为tick。
当我们将函数传递给process.nextTick() 时,我们指示引擎在当前操作结束时,在下一个事件循环tick开始之前调用此函数:
process.nextTick(() => {
// do something
});
事件循环正忙于处理当前函数代码。当该操作结束时,JS引擎将运行该操作期间传递给nextTick调用的所有函数。
这是我们可以告诉JS引擎异步处理函数(在当前函数之后)的方法,但要尽快,而不是将其排队。
调用setTimeout(()=>{},0) 将在下一个刻度结束时执行该函数,比使用nextTick() 要晚得多,后者会对调用进行优先级排序,并在下一刻度开始前执行。
当您希望确保在下一次事件循环迭代中该代码已经执行时,请使用nextTick() 。
事件顺序示例:
console.log("Hello => number 1");
setImmediate(() => {
console.log("Running before the timeout => number 3");
});
setTimeout(() => {
console.log("The timeout running last => number 4");
}, 0);
process.nextTick(() => {
console.log("Running at next tick => number 2");
});
输出结果:
Hello => number 1
Running at next tick => number 2
Running before the timeout => number 3
The timeout running last => number 4
2、setTimeout()
在编写JavaScript代码时,您可能希望延迟函数的执行。
这是setTimeout的工作。指定一个稍后执行的回调函数,以及一个表示希望它稍后运行的值(以毫秒为单位):
setTimeout(() => {
// runs after 2 seconds
}, 2000);
setTimeout(() => {
// runs after 50 milliseconds
}, 50);
此语法定义了一个新函数。你可以调用你想要的任何其他函数,也可以传递一个现有的函数名和一组参数:
const myFunction = (firstParam, secondParam) => {
// do something
console.log(firstParam + ' ' + secondParam)
};
// runs after 2 seconds
setTimeout(myFunction, 2000, "zhang", "san");
setTimeout返回计时器id。通常不使用此id,但您可以存储此id,如果您想删除此计划的函数执行,则可以将其清除:
const id = setTimeout(() => {
// should run after 2 seconds
}, 2000);
// I changed my mind
clearTimeout(id);
3、零延迟
如果将超时延迟指定为0,回调函数将尽快执行,但在当前函数执行之后:
setTimeout(() => {
console.log('after ');
}, 0);
console.log(' before ');
代码将会打印:
before
after
这对于避免在密集任务上阻塞CPU,并通过在调度器中对函数进行排队来在执行繁重计算时执行其他函数特别有用。
一些浏览器(IE和Edge)实现了setImmediate() 方法,该方法具有相同的功能,但它不是标准的,在其他浏览器上不可用。但这是Node.js中的一个标准函数。
在chrome 浏览中,测试下:
4、setInterval()
setInterval是一个类似于setTimeout的函数,但不同之处在于:它不会运行回调函数一次,而是将以您指定的特定时间间隔(以毫秒为单位)永远运行回调函数:
setInterval(() => {
// runs every 2 seconds
}, 2000);
上面的函数每2秒运行一次,除非您告诉它停止,否则使用clearInterval将setInterval返回的interval id传递给它:
const id = setInterval(() => {
// runs every 2 seconds
}, 2000);
clearInterval(id);
通常在setInterval回调函数内部调用clearInterval,让它自动决定是应该再次运行还是停止。
5、递归setTimeout
setInterval每n毫秒启动一个函数,而不考虑函数何时完成执行。
如果一个函数总是花费相同的时间,那么一切都很好:
根据网络条件的不同,该函数可能需要不同的执行时间,例如:
也许一个长的执行与下一个重叠:
为了避免这种情况,您可以安排在回调函数完成时调用递归setTimeout:
const myFunction = () => {
// do something
setTimeout(myFunction, 1000);
};
setTimeout(myFunction, 1000);
为了实现这种情况:
setTimeout和setInterval在Node.js中通过Timers模块提供。
Node.js还提供了setImmediate(),相当于使用setTimeout(()=>{},0),主要用于处理Node.js事件循环。
6、setImmediate()
当您想异步执行某段代码,但要尽快执行时,一种选择是使用Node.js提供的setImmediate()函数:
setImmediate(() => {
// run something
});
作为setImmediate() 参数传递的任何函数都是在事件循环的下一次迭代中执行的回调。
setImmediate() 与setTimeout(() => { }, 0)(传递0ms超时)以及process.nextTick() 和Promise.then() 有何不同?
传递给process.nextTick() 的函数将在当前操作结束后,在事件循环的当前迭代中执行。这意味着它将始终在setTimeout和setImmediate之前执行。
延迟0ms的setTimeout() 回调与setImmediate() 非常相似。执行顺序将取决于各种因素,但它们都将在事件循环的下一次迭代中运行。
process.nextTick回调被添加到process.nextTick队列中。Promise.then() 回调被添加到promises微任务队列中。将setTimeout、setImmediate回调添加到宏任务队列中。
事件循环先执行process.nextTick队列中的任务,然后执行promise微任务队列,然后执行宏任务队列。
以下是一个示例,用于显示setImmediate() 、process.nextTick() 和Promise.then() 之间的顺序:
const baz = () => console.log('baz');
const foo = () => console.log('foo');
const zoo = () => console.log('zoo');
const start = () => {
console.log('start');
setImmediate(baz);
new Promise((resolve, reject) => {
resolve('bar');
}).then((resolve) => {
console.log(resolve);
process.nextTick(zoo);
});
process.nextTick(foo);
};
start();
// start foo bar zoo baz
7、Node.js 事件派发器
如何在Node.js中使用自定义事件
如果你在浏览器中使用JavaScript,你就会知道用户的大部分交互是通过事件来处理的:鼠标点击、键盘按钮按下、对鼠标移动的反应等等。
在后端,Node.js为我们提供了使用事件模块构建类似系统的选项。
这个模块特别提供了EventEmitter类,我们将使用它来处理我们的事件。
初始化使用:
const EventEmitter = require('events');
const eventEmitter = new EventEmitter();
该对象公开了on和emit方法以及其他许多方法。
- emit 用于触发事件
- on 用于添加一个回调函数,该函数将在事件被触发时执行
例如,让我们创建一个start事件,作为提供一个示例,我们只需看控制台的输出:
eventEmitter.on('start', () => {
console.log('started');
});
我们运行:
eventEmitter.emit('start');
您可以将参数作为附加参数传递给emit(),从而将参数传递给事件处理程序
eventEmitter.on('start', (start, end) => {
console.log(`started from ${start} to ${end}`);
});
eventEmitter.emit('start', 1, 100);
EventEmitter对象还公开了其他几种与事件交互的方法,如
- once() :添加一次性侦听器
- removeLister() / of() :从事件中删除事件侦听器
- removeAllListeners() :删除事件的所有侦听器
您可以在官方文档中关于这些方法的信息。
https://nodejs.dev/en/api/events/