【问题标题】:what's the differences between these two async func in nodejs?nodejs中这两个异步函数有什么区别?
【发布时间】:2019-01-31 11:54:00
【问题描述】:
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,这两个异步回调的行为应该相同, 但结果不同, 幕后原因是什么?

【问题讨论】:

  • 当监听事件触发时,两个监听处理程序都被添加到事件队列中——直到事件队列清空next tick queue才会被处理

标签: javascript node.js asynchronous event-loop libuv


【解决方案1】:

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

您的第二个是完全相同的事件的两个事件侦听器。因此,这两个侦听器保证在同一个滴答声中一个接一个地被调用。当事件侦听器对象发出事件时,它会一个接一个地同步调用该事件的所有侦听器,所有侦听器都在同一个滴答声上。这就是为什么你会在未来的报价中出现34 之前得到12

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

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

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

}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-20
    • 2013-04-03
    • 1970-01-01
    • 1970-01-01
    • 2018-11-01
    相关资源
    最近更新 更多