nodejs中这两个异步函数之间的区别是什么?

问题描述 投票:4回答:1
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,这两个异步回调应该表现相同,但结果不同,这个场景背后的原因是什么?

javascript node.js asynchronous event-loop libuv
1个回答
4
投票

第一个是两个完全独立的异步fs.readFile()操作之间的竞赛。无论哪一个先完成,都可能会在另一个之前获得它们的控制台日志。因为这些操作需要花费一些可测量的时间,并且它们都必须完成相同数量的工作,所以您首先开始的操作可能会先完成,这就是您所看到的。但是,从技术上讲,它是两个异步操作之间的不确定竞争,它们可以按任何顺序完成。由于一个可能在另一个之前稍微完成,它的完成回调将在另一个之前调用,并且也可能在下一个滴答发生之前还没有完成第二个回调,这就是为什么你看到两个日志消息来自哪一个先完成。

你的第二个是完全相同的事件的两个事件监听器。因此,保证这两个听众一个接一个地在相同的音符上调用。当一个事件监听器对象发出一个事件时,它会一个接一个地同步调用该事件的所有监听器,所有这些都在同一个tick上。这就是为什么你会在12之前获得3然后4,这将在未来的时间点出现。

不应该将eventEmitter对象与事件队列混淆。它们不是同一件事。在这种情况下,您的服务器对象是eventEmitter的子类。服务器内部的一些代码决定将listening事件发送给服务器的eventEmitter的侦听器。发出事件的决定很可能是来自事件队列的一些异步操作的结果。但是,要实际发送到eventEmitter,这只是对已注册侦听器的同步函数调用。事件队列不涉及此事件。在eventEmitter代码的内部,它实际上有一个for循环,它循环遍历匹配的事件处理程序并一个接一个地调用每一个。这就是为什么你得到1,然后2

实际上,这里是在EventEmitter类定义中的code reference方法中的.emit(),它显示了通过匹配的侦听器循环并同步调用它们。而且,这是一段代码,一个接一个地调用每个监听器:

const len = handler.length;
const listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i)
  Reflect.apply(listeners[i], this, args);

}

© www.soinside.com 2019 - 2024. All rights reserved.