在一段时间 t 后或事件触发时解决承诺,以先发生者为准

Resolving a promise after some time t or if an event fires, whichever occurs first

我有一个名为 wait 的函数,它接受以毫秒为单位的时间和 returns 一个承诺。如果 myEvent 被解雇或一段时间后 ms 以先发生者为准,承诺应该得到解决。目前我正在做这样的事情,我认为这不是正确的做法。

async function wait(ms) {
    return new Promise((res) => {
        myEmitter.on('myEvent', res);
        setTimeout(res, ms);
    });
}

有更好的方法吗?

你做的很好。¹一旦承诺被确定(通过第一次调用 resolve 函数或 reject 函数,使用它们的常用名称),调用 resolve/reject 函数再次不执行任何操作。所以你有的是简单的写法。

如果你在事件处理程序或超时处理程序中做一些有副作用的事情,比如 res(doSomethingAndReturnValue())that 不是一个好主意,因为 doSomethingAndReturnValue() 部分仍然执行并有其副作用。但在您的示例中,您没有这样做。


¹ 三个相当小的音符:

  1. 我假设当你的事件处理程序被调用时,一些信息被传递给处理程序。由于您直接使用 res 作为处理程序,它会收到该信息。因此,您将其设置为使用事件处理程序获得的值(如果事件首先触发)或使用 undefined(如果计时器首先触发)来实现承诺,这意味着使用它的代码会看到不同的实现值.

  2. 通常我希望超时是拒绝而不是满足,但这取决于您的用例。

  3. 您的函数不需要 async 修饰符,因为它从不使用 await。函数不需要 async 只是因为它 return 是一个承诺。从大约一年前对规范的规范性更改开始,如果您 return 来自 async 函数的本机承诺,它不再有任何区别(它过去常常延迟事情 非常),但没有理由。

您的代码是正确的。一个 promise 只能被解析一次——如果你解析它两次,第二次实际上是一个空操作。

话虽如此,您可以使用具有相同想法的 Promise.race 作为替代方案。它以承诺作为输入,并会产生一个承诺,该承诺会在承诺的第一个输入结算时结算。

例如,点击此处的按钮结算一个承诺,但它也会在 10 秒内结算:

let buttonResolve;
let buttonPromise = new Promise(res => buttonResolve = res);

document.querySelector("button")
  .addEventListener("click", buttonResolve)

let timedPromise = new Promise(res => setTimeout(res, 10000));
let start = Date.now();
Promise.race([buttonPromise, timedPromise])
  .then(() => console.log(`finished in ${(Date.now() - start) / 1000}s`))
<button>Click me</button>