const fs = require("fs");
fs.readFile("aa.js", () => {
console.log("1");
process.nextTick(() => {
console.log("3");
});
});
fs.readFile("aa.js", () => {
console.log("2");
process.nextTick(() => {
console.log("4");
});
});
//结果是1 3 2 4
const net = require("net");
const server = net.createServer(() => {}).listen(8080);
server.on("listening", () => {
console.log("1");
process.nextTick(() => {
console.log("3");
});
});
server.on("listening", () => {
console.log("2");
process.nextTick(() => {
console.log("4");
});
});
//结果是1 2 3 4
IMO,这两个异步回调的行为应相同, 但结果不同 幕后原因是什么?
答案 0 :(得分:4)
第一个是两个完全独立的异步fs.readFile()
操作之间的竞争。哪个先完成,很可能先获得两个控制台日志。因为这些操作需要花费可测量的时间,并且它们都必须执行完全相同的工作量,所以您最先开始的操作可能会先完成,这就是您所看到的。但是,从技术上讲,这是两个异步操作之间的不确定竞争,它们可以按任何顺序完成。由于一个可能要比另一个稍早完成,因此它的完成回调将在另一个之前被调用,并且第二个也可能在下一个滴答发生之前还没有完成,所以这就是为什么您看到两个日志消息都来自哪个先完成。
您的第二个侦听器是完全相同的事件的两个事件侦听器。因此,可以确保在同一滴答中依次调用这两个侦听器。当事件侦听器对象发出事件时,它会在同一滴答声中一个接一个地同步调用该事件的所有侦听器。这就是为什么在未来的价格变动中出现1
和2
之前3
和4
之前的原因。
不应将eventEmitter对象与事件队列混淆。它们不是同一件事。在这种情况下,您的服务器对象是eventEmitter的子类。服务器内部的某些代码决定向服务器的eventEmitter的侦听器发出listening
事件。发出事件的决定很可能是事件队列中某些异步操作的结果。但是,实际上要向eventEmitter发出,这只是对已注册侦听器的同步函数调用。事件队列不参与其中。在eventEmitter代码的内部,它实际上有一个for
循环,该循环遍历匹配的事件处理程序,并一个接一个地调用。这就是为什么您先得到1
,然后得到2
的原因。
实际上,这是EventEmitter类定义的.emit()
方法内的code reference,它显示了遍历匹配的侦听器并进行同步调用。而且,这是该代码的一个片段,一个接一个地调用每个侦听器:
const len = handler.length;
const listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i)
Reflect.apply(listeners[i], this, args);
}