为了更好地理解异步和纤程,我想知道是否有一个全局变量在事件循环的每一轮都会递增。
我希望能够看到在每个 console.log 语句中打印出不同的值,显然我们不能依赖系统时间来做到这一点。
function getEventLoopCounter () { /* magic occurs */ }
// Turn 1
console.log("I'm on loop number: ", getEventLoopCounter());
// Turn > 1
setTimeout(function(){
console.log("I'm on different loop: ", getEventLoopCounter());
}, 0);
也许来自节点定时器模块
的
setImmediate
可以工作。引用自 setImmediate
的文档:
安排 I/O 事件后“立即”执行回调 回调以及 setTimeout 和 setInterval 之前。返回一个 可以与clearImmediate()一起使用的immediateObject。 (可选)你 还可以将参数传递给回调。
即时回调按照它们出现的顺序排队 创建的。每个事件循环都会处理整个回调队列 迭代。如果您从正在执行的内部对立即数进行排队 回调,该立即事件在下一个事件循环之前不会触发 迭代。
使用函数闭包和递归,你可以这样做:
var eventLoopCounter = 0;
setImmediate(function incrementEventLoopCounter() {
eventLoopCounter++;
setImmediate(incrementEventLoopCounter);
});
// Logging the total number of iterations every second.
setInterval(function() {
console.log('The event loop has finished '
+ eventLoopCounter
+ ' iterations.');
}, 1000);
顺便说一句,在
timers
模块文档的开头,它指出:
所有定时器函数都是全局函数。您不需要 require() 这个模块才能使用它们。
这就是为什么
setImmediate
无需 timers
模块即可工作。
process.nextTick
做类似的事情,但我收到了一个错误,前面有一系列有用的警告消息,这些消息将我指向上面的函数:
...
(node) warning: Recursive process.nextTick detected. This will break in the next version of node. Please use setImmediate for recursive deferral.
(node) warning: Recursive process.nextTick detected. This will break in the next version of node. Please use setImmediate for recursive deferral.
RangeError: Maximum call stack size exceeded
使用 setImmediate 在 Node.js 中效果很好,但不幸的是它不是 JavaScript 的标准。
还有一种使用微任务的替代解决方案:
const getEventLoopCounter = (() => {
let counter = 0;
let pending = false;
const next = typeof queueMicrotask === 'function' ?
queueMicrotask :
callback => Promise.resolve().then(callback); // shim for old browsers
return () => {
if (!pending) {
pending = true;
next(() => {
pending = false;
counter++;
});
}
return counter;
};
})();
(如果您需要跟踪宏任务,请使用
setTimeout
作为next
功能)
从新循环访问时,此代码保证返回新值,反之亦然 - 如果我们尚未退出下一个循环,则它返回未更改的值。
但请注意,它无法给出确切的经过的周期数。例如,如果有 100 个周期没有访问 getEventLoopCounter,则其新值将仅比前一个大 1,而不是 100。