在 Node 中异步等待特定事件

时间:2020-12-29 02:24:50

标签: javascript node.js async-await

我有一个现有的 API,它运行一个需要一些时间的任务,然后在作业完成时使用 EventEmitter 发出一个事件。出于我们的目的,我们可以假设它看起来像这样。

const { EventEmitter } = require('events');

class Example extends EventEmitter {
  longRunningJob() {
    // Assume some long-running task is happening here...
    setTimeout(() => {
      this.emit('job_completed');
    }, 1000);
  }
}

现在,我正在编写一个 async 函数,我想按顺序执行几个这样的任务。一种幼稚的方法可能是

const example = new Example();
console.log("Starting jobs...");
example.longRunningJob();
example.once('job_completed', () => {
  console.log("One");
  example.longRunningJob();
  example.once('job_completed', () => {
    console.log("Two");
    example.longRunningJob();
    example.once('job_completed', () => {
      console.log("Done :)");
    });
  });
});

但是这与促使我们发明 async / await 的可读性问题相同。因此,尝试将这种方法包装在某种 Promise 中是有意义的,我已经完成了如下操作。

const onEvent = function(obj, eventName) {
  let resolve;
  const promise = new Promise(function(res, rej) { resolve = res; });
  obj.once(eventName, function() { resolve(); });
  return promise;
};

const example = new Example();
console.log("Starting jobs...");
example.longRunningJob();
await onEvent(example, 'job_completed');
console.log("One");
example.longRunningJob();
await onEvent(example, 'job_completed');
console.log("Two");
example.longRunningJob();
await onEvent(example, 'job_completed');
console.log("Done :)");

我们已经修复了“失控缩进”的问题,但这仍然不能让我很满意。 onEvent 是变异变量和让 Promise 解析函数逃避范围的混乱组合,这感觉很脆弱。此外,这感觉像是一个并不少见的问题:等待特定事件的愿望。所以我的问题是 (a) 此功能是否以更强大的方式内置到 Node 中,以及 (b) 如果不是,是否有更强大、更少被黑客攻击的方式来等待特定事件?

1 个答案:

答案 0 :(得分:0)

一种简化是立即返回 Promise 而不是首先分配给外部 resolve 变量:

const onEvent = (obj, eventName) => new Promise((resolve) => {
  obj.once(eventName, resolve);
};

您也可以使用循环代替多次写出 onEventlongRunningJob

const example = new Example();
console.log("Starting jobs...");
for (let i = 0; i < 3; i++) {
  example.longRunningJob();
  await onEvent(example, 'job_completed');
  console.log(i);
}
console.log('done');

如果可能,还可以考虑将 Example 更改为返回 Promise 而不是使用回调的方法:

const example = new Example();
console.log("Starting jobs...");
for (let i = 0; i < 3; i++) {
  await example.queueJob();
  console.log(i);
}
console.log('done');

这确实是目前最好的解决方案(让该方法返回一个在工作完成时解析的 Promise),但如果它是第 3 方 API,则可能无法更改其代码。