使用 promise 等待触发事件 [英] Using promises to await triggered events
问题描述
在下面的代码中,thing
是一个我无法控制的外部对象;我无法改变 thing
事件系统的工作方式.当 fn
被调用时,我们返回一个承诺,它的执行者监听一个事件,然后开始等待一系列最终导致该事件被触发的函数:
In the following code, thing
is an external object that I don't control; I can't change how the event system of thing
works. When fn
is called, we return a promise whose executor listens to an event and then begins awaiting a series of functions which eventually result in that event being triggered:
function fn() {
return new Promise(async function(resolve, reject) {
// This handler must be attached before `c` is called
thing.once('myEvent', function(e) {
resolve(e.data); // done
});
// The order of these functions calls is important,
// and they may produce errors that need to be handled.
await a();
await b();
await c(); // this causes myEvent
});
}
这很好用,但是 有人告诉我 这是一个promise 反模式,我应该让fn
成为一个async
函数.我该怎么做?如果我使 fn
成为一个 async
函数,那么我如何从事件处理程序中解析 e.data
?
This works fine, but I've been told that this is a promise anti-pattern and that I should make fn
an async
function. How would I do this? If I made fn
an async
function, then how could I resolve e.data
from within the event handler?
我接受了 Bergi 的回答,因为它有助于解释反模式以及它如何适用于这种情况.话虽如此,我认为上面的代码更具可读性,并明确显示了正在发生的事情,所以我将保持原样.这不是谴责最佳实践的菜鸟,只是对于我的用例,遵循规则会使事情变得比它们需要的更复杂.当然,这让我对某些问题持开放态度,但我只能忍受它,直到找到更好的方法来做到这一点.
I've accepted Bergi's answer because it is helpful in explaining the anti-pattern and how it applies to this scenario. Having said that, I think the code above is more readable and shows explicitly what's happening so I'm going to keep it as is. This isn't the noob denouncing the best practices, it's just that for my use-case, following the rules makes things more complicated than they need to be. Of course, this leaves me open to certain problems, but I'll just have to live with that until I find a better way to do this.
推荐答案
不要在 Promise
构造函数中做任何 await
- 你只应该做承诺那里的异步回调:
Don't do any await
ing inside the Promise
constructor - you only should do the promisification of the asynchronous callback in there:
async function fn() {
await a();
await b();
await c(); // this causes myEvent
return new Promise(function(resolve, reject) {
thing.once('myEvent', function(e) {
resolve(e.data); // done
});
});
}
通常在 Promise
执行器回调中调用启动进程并最终导致发出事件的东西(以捕获同步异常),但通常它不返回承诺就像你的 c
函数一样.
The thing that starts the process which eventually causes the event to be emitted is usually called inside the Promise
executor callback as well (to catch synchronous exceptions), but usually it doesn't return a promise like your c
function does.
也许这能更好地表达意图:
Maybe this expresses the intent better:
async function fn() {
await a();
await b();
const {data} = await new Promise(resolve => {
thing.once('myEvent', resolve);
thing.c(); // this causes myEvent
});
return data;
}
当然,这假设您只需要在调用其他事件时开始侦听事件.如果您希望事件在此之前触发,那么您本质上会与并行执行竞争 - 在这种情况下,我建议使用 Promise.all
:
Of course this assumes that you only need to start listening to the event when you've called the other ones. If you expect the event to fire before that, you essentially have a race with parallel execution - I'd recommend to use Promise.all
in that case:
async function fn() {
await a();
await b();
const [{data}, cResult] = await Promise.all([
new Promise(resolve => thing.once('myEvent', resolve)),
c()
]);
return data;
}
如果您有节点 v11.13.0 或更高版本,您可以使用 events.once
方法 这样您就不必自己构建承诺 - 并且它还可以正确处理错误事件:
If you have node v11.13.0 or higher you can use the events.once
method so that you don't have to build the promise yourself - and it also handles error events correctly:
import { once } from 'events';
async function fn () {
await a()
await b()
await c()
await once(thing, 'myEvent')
}
这篇关于使用 promise 等待触发事件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!