如何在异步上下文中捕获异常

How to catch exception in async context

当上下文为 async.

时,我遇到异常捕获问题

这是我的最小可重现代码

const { EventEmitter } = require('events');
const delaySync = function(ttd) {
  return new Promise((resolve) => {
    setTimeout(() => resolve(0), ttd);
  });
};

const myEvent = new EventEmitter();
myEvent.on('something', async () => {
  console.log('Event triggered');

  // Just delay for 100ms and then throw
  await delaySync(100);

  throw new Error('Something happened');
});

describe('Events', () => {

  it('should catch error', async () => {
    await expect(myEvent.emit('something')).rejects.toThrow('Something happened');
  });

});

基本上,我有一个事件发射器。它有一个附加在异步事件 'something' 上的回调。内部有一些异步作业,在 100ms 延迟后会抛出错误。

通常我会在玩笑中使用 await expect(fn).rejects.toThrow(...) 捕获此类错误,但是这种情况不同,因为 expect fn 需要一个承诺,而 myEvent.emit('something') 是常规的非承诺上下文。

在这种情况下,我遇到了一个错误

● Events › should catch error

    expect(received).rejects.toThrow()

    Matcher error: received value must be a promise or a function returning a promise

    Received has type:  boolean
    Received has value: true
...
(node:20242) UnhandledPromiseRejectionWarning: Error: Something happened

我也用 try-catch 块尝试过,但它不起作用(根本无法捕获);

try {
  myEvent.emit('something');
  await delaySync(250);
} catch (e) {
  expect(e.message).toBe('Something happened');
}

我试过了

 // Not throwing because exception is not happening right away
 expect(() => myEvent.emit('something')).toThrow('Something happened');

还有这个

expect(async () => {   
  // Will thow after 100ms
  myEvent.emit('something');
  await delaySync(250);
}).toThrow('Something happened');

但他们中的任何一个都不走运。

关于如何在这种情况下捕获错误的任何想法?或任何解决方法?

Eventemitter 完全 sync 并且没有解决方法。你能做的就是用eventemitter2模块代替原来的Eventemitterclass。它与 Eventemitter 完全兼容,并允许您使用 async 操作。

var EventEmitter = require('eventemitter2');

const delaySync = function (ttd) {
  return new Promise((resolve) => {
    setTimeout(() => resolve(0), ttd);
  });
};

const myEvent = new EventEmitter();
myEvent.on('something', async () => {
  console.log('Event triggered');

  // Just delay for 100ms and then throw
  await delaySync(100);

  throw new Error('Something happened');

  // for async emitters pass promisify true.
}, { promisify: true });

describe('Events', () => {
  it('should catch error', async () => {
    await expect(myEvent.emit('something')).rejects.toThrow('Something happened');
  });

}