使用 promise 等待触发事件 [英] Using promises to await triggered events

查看:45
本文介绍了使用 promise 等待触发事件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在下面的代码中,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 awaiting 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆