NodeJS UnhandledPromiseRejectionWarning

Jzo*_*zop 113 javascript mocha.js node.js promise chai

所以,我正在测试一个依赖于事件发射器的组件.为此,我想出了一个使用Promise with Mocha + Chai的解决方案:

it('should transition with the correct event', (done) => {
  const cFSM = new CharacterFSM({}, emitter, transitions);
  let timeout = null;
  let resolved = false;
  new Promise((resolve, reject) => {
    emitter.once('action', resolve);
    emitter.emit('done', {});
    timeout = setTimeout(() => {
      if (!resolved) {
        reject('Timedout!');
      }
      clearTimeout(timeout);
    }, 100);
  }).then((state) => {
    resolved = true;
    assert(state.action === 'DONE', 'should change state');
    done();
  }).catch((error) => {
    assert.isNotOk(error,'Promise error');
    done();
  });
});
Run Code Online (Sandbox Code Playgroud)

在控制台上我得到一个'UnhandledPromiseRejectionWarning',即使拒绝函数被调用,因为它立即显示消息'AssertionError:Promise error'

(node:25754)UnhandledPromiseRejectionWarning:未处理的promise拒绝(拒绝id:2):AssertionError:Promise错误:expect {Object(message,showDiff,...)}是假的1)应该转换为正确的事件

然后,在2秒后我得到了

错误:超过2000毫秒的超时.确保在此测试中调用done()回调.

自从执行catch回调以来,这甚至更奇怪.(我认为由于某种原因,断言失败阻止了其余的执行)

现在有趣的是,如果我注释掉assert.isNotOk(error...)测试运行正常,控制台中没有任何警告.它执行捕获的意义上仍然"失败".
但是,我仍然无法理解这些错误.有人可以开导我吗?

rob*_*lep 141

问题是由此造成的:

.catch((error) => {
  assert.isNotOk(error,'Promise error');
  done();
});
Run Code Online (Sandbox Code Playgroud)

如果断言失败,则会抛出错误.此错误将导致done()永远不会被调用,因为代码在它之前出错了.这就是造成超时的原因.

"未处理的承诺拒绝"也由断言失败造成的,因为如果一个错误在被抛出catch()的处理程序,并没有后续的catch()处理程序,错误将得到吞噬(说明这篇文章).该UnhandledPromiseRejectionWarning警告提醒你这个事实.

一般来说,如果你想在Mocha中测试基于promise的代码,你应该依赖Mocha本身可以处理promise的事实.你不应该使用done(),而是从你的测试中返回一个承诺.然后摩卡会捕捉到任何错误.

像这样:

it('should transition with the correct event', () => {
  ...
  return new Promise((resolve, reject) => {
    ...
  }).then((state) => {
    assert(state.action === 'DONE', 'should change state');
  })
  .catch((error) => {
    assert.isNotOk(error,'Promise error');
  });
});
Run Code Online (Sandbox Code Playgroud)

  • 对于任何好奇的人来说,茉莉花也是如此. (6认同)

dan*_*y74 9

当与sinon衔接时我得到了这个错误.

解决方法是在解析或拒绝带存根的promises时使用npm package sinon-as-promised.

代替 ...

sinon.stub(Database, 'connect').returns(Promise.reject( Error('oops') ))
Run Code Online (Sandbox Code Playgroud)

使用 ...

require('sinon-as-promised');
sinon.stub(Database, 'connect').rejects(Error('oops'));
Run Code Online (Sandbox Code Playgroud)

还有一种解决方法(注意结尾的s).

http://clarkdave.net/2016/09/node-v6-6-and-asynchronously-handled-promise-rejections


Mat*_*ndo 9

如果断言不正确,Mocha中的断言库通过抛出错误来工作.抛出错误会导致拒绝承诺,即使在提供给catch方法的执行程序函数中抛出也是如此.

.catch((error) => {
  assert.isNotOk(error,'Promise error');
  done();
});
Run Code Online (Sandbox Code Playgroud)

在上面的代码中,对象error进行求值,true因此断言库抛出一个错误......它永远不会被捕获.由于该错误,该done方法永远不会被调用.Mocha的done回调接受这些错误,因此您可以简单地结束Mocha中的所有承诺链.then(done,done).这确保始终调用done方法,并且报告错误的方式与Mocha捕获同步代码中的断言错误的方式相同.

it('should transition with the correct event', (done) => {
  const cFSM = new CharacterFSM({}, emitter, transitions);
  let timeout = null;
  let resolved = false;
  new Promise((resolve, reject) => {
    emitter.once('action', resolve);
    emitter.emit('done', {});
    timeout = setTimeout(() => {
      if (!resolved) {
        reject('Timedout!');
      }
      clearTimeout(timeout);
    }, 100);
  }).then(((state) => {
    resolved = true;
    assert(state.action === 'DONE', 'should change state');
  })).then(done,done);
});
Run Code Online (Sandbox Code Playgroud)

我赞扬这篇文章是为了在Mocha中测试promises时使用.then(done,done)的想法.


gsa*_*edo 5

对于那些UnhandledPromiseRejectionWarning在测试环境之外寻找错误/警告的人,可能是因为代码中没有人照顾承诺中的最终错误:

例如,此代码将显示此问题中报告的警告:

new Promise((resolve, reject) => {
  return reject('Error reason!');
});
Run Code Online (Sandbox Code Playgroud)

(node:XXXX) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Error reason!

并添加.catch()或处理错误应解决警告/错误

new Promise((resolve, reject) => {
  return reject('Error reason!');
}).catch(() => { /* do whatever you want here */ });
Run Code Online (Sandbox Code Playgroud)

或在then函数中使用第二个参数

new Promise((resolve, reject) => {
  return reject('Error reason!');
}).then(null, () => { /* do whatever you want here */ });
Run Code Online (Sandbox Code Playgroud)

  • 当然,但我认为在现实生活中我们通常不只使用 new Promise((resolve,reject) => { returnreject('Error Reason!'); })` 而是在函数 `function test() { return new Promise((resolve,reject) => { returnreject('Error Reason!'); });}` 所以在函数内部我们不需要使用 `.catch()` 但要成功处理错误就足够了在调用该函数 `test().catch(e => console.log(e))` 或异步/等待版本 `try { wait test() } catch (e) { console.log(e) }` 时使用 (3认同)