NodeJS UnhandledPromiseRejectionWarning


134

そこで、イベントエミッターに依存するコンポーネントをテストしています。そうするために、私はMocha + ChaiでPromisesを使用するソリューションを思いつきました。

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();
  });
});

コンソールに「UnhandledPromiseRejectionWarning」が表示されますが、「AssertionError:Promise error」というメッセージが即座に表示されるため、リジェクト関数が呼び出されます。

(node:25754)UnhandledPromiseRejectionWarning:Unhandled promise rejection(rejection id:2):AssertionError:Promise error:expected {Object(message、showDiff、...)} to be falsy 1)should transition with the correct event

そして、2秒後に私は

エラー:2000msのタイムアウトを超えました。このテストでdone()コールバックが呼び出されていることを確認してください。

catchコールバックが実行されたので、これはさらに奇妙です(アサートの失敗により、残りの実行が妨げられたと思います)

面白いことに、コメントアウトするassert.isNotOk(error...)と、コンソールに警告が表示されずにテストが正常に実行されます。キャッチを実行するという意味では、依然として「失敗」します。
しかし、それでも私はこれらのエラーを約束とともに理解することはできません。誰かが私を啓発できますか?


一番最後の行にもう1組の閉じ中括弧と括弧があると思います。それらを削除して、もう一度やり直してください。
Redu

4
これはとてもクールです。新しい未処理の拒否警告は、実際のバグを検出し、人々の時間を節約します。ここでたくさん勝ちなさい。この警告がなければ、説明なしでテストがタイムアウトになります。
Benjamin Gruenbaum 2016

回答:


161

問題はこれによって引き起こされます:

.catch((error) => {
  assert.isNotOk(error,'Promise error');
  done();
});

アサーションが失敗すると、エラーがスローされます。このエラーはdone()、コードがその前にエラーになったため、呼び出されることはありません。これがタイムアウトの原因です。

「未処理の約束の拒絶は、」エラーがでスローされた場合ためにも、失敗したアサーションによって引き起こされるcatch()ハンドラ、およびそれに続くがないcatch()ハンドラは(で説明したように、エラーが飲み込まれます。この記事)。UnhandledPromiseRejectionWarning警告は、この事実を警告されます。

一般に、ProchaベースのコードをMochaでテストする場合は、Mocha自体がすでにPromiseを処理できるという事実に依存する必要があります。を使用するべきではありませんdone()が、代わりにテストからpromiseを返します。その後、Mochaはエラー自体をキャッチします。

このような:

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');
  });
});

7
好奇心が強い人にとっては、これはジャスミンにも当てはまります。
Nick Radford

@robertklep予期しないエラーだけでなく、エラーが発生してもキャッチされませんか?失敗を主張しようとすると、このスタイルは機能しないと思います。
TheCrazyProgrammer 2017年

1
@TheCrazyProgrammer catchハンドラはおそらく2番目の引数としてに渡されますthen。ただし、OPの意図が完全にわからないため、そのままにしました。
robertklep 2017年

1
また、ジャスミンに興味がある人はdone.fail('msg')、この場合に使用します。
パヴェル

メインコードとアサーションの行き先の完全な例をここに示すことができますが、ここではそれほど明確ではありません。特に、コードで元のサービス/プロミスコールからの値をアサートする場合。私たちのコードの実際の約束は、この他の「モカ約束」の中に含まれていますか?
bjm88 2018年

10

sinonでスタブすると、このエラーが発生しました。

修正は、スタブを使用してpromiseを解決または拒否するときに npmパッケージsinon-as-promisedを使用することです。

の代わりに ...

sinon.stub(Database, 'connect').returns(Promise.reject( Error('oops') ))

使用する ...

require('sinon-as-promised');
sinon.stub(Database, 'connect').rejects(Error('oops'));

resolvesメソッドもあります(最後のに注意してください)。

http://clarkdave.net/2016/09/node-v6-6-and-asynchronously-handled-promise-rejectionsを参照してください


1
Sinonには、バージョン2の時点でスタブのメソッド「解決」および「拒否」が含まれるようになりました。npmjs.com/package/sinon-as-promisedを参照してください。私はまだ答えを+1しました-私はこれについて知りませんでした。
Andrew

9

Mochaのアサーションライブラリは、アサーションが正しくなかった場合にエラーをスローすることで機能します。エラーをスローすると、catchメソッドに提供されたエグゼキューター関数でスローされた場合でも、promiseが拒否されます。

.catch((error) => {
  assert.isNotOk(error,'Promise error');
  done();
});

上記のコードでは、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);
});

MochaでPromiseをテストするときに.then(done、done)を使用するという考えについて、この記事を評価します。


6

UnhandledPromiseRejectionWarningテスト環境の外でエラー/警告を探している人にとっては、おそらくコード内の誰もが最終的なエラーをプロミスで処理していないためと考えられます。

たとえば、このコードはこの質問で報告された警告を表示します:

new Promise((resolve, reject) => {
  return reject('Error reason!');
});

(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 */ });

または、then関数の2番目のパラメーターを使用する

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

1
もちろん、私は実際には通常、関数ではなく関数を使用new Promise((resolve, reject) => { return reject('Error reason!'); })するfunction test() { return new Promise((resolve, reject) => { return reject('Error reason!'); });}ので、関数内では使用する必要はありませんが、.catch()エラーを正常に処理するには、その関数test().catch(e => console.log(e))または非同期/ try { await test() } catch (e) { console.log(e) }
待機

1

私はこの問題に直面しました:

(ノード:1131004)UnhandledPromiseRejectionWarning:未処理のプロミス拒否(リジェクションID:1):TypeError:res.jsonは関数ではありません(ノード:1131004)DeprecationWarning:未処理のプロミス拒否は非推奨です。将来は、処理されないpromiseの拒否により、Node.jのプロセスがゼロ以外の終了コードで終了します。

それは私の間違いでした、私はのresオブジェクトを置き換えていたため、結果にthen(function(res)変更さresれ、現在は機能しています。

違う

module.exports.update = function(req, res){
        return Services.User.update(req.body)
                .then(function(res){//issue was here, res overwrite
                    return res.json(res);
                }, function(error){
                    return res.json({error:error.message});
                }).catch(function () {
                   console.log("Promise Rejected");
              });

補正

module.exports.update = function(req, res){
        return Services.User.update(req.body)
                .then(function(result){//res replaced with result
                    return res.json(result);
                }, function(error){
                    return res.json({error:error.message});
                }).catch(function () {
                   console.log("Promise Rejected");
              });

サービスコード:

function update(data){
   var id = new require('mongodb').ObjectID(data._id);
        userData = {
                    name:data.name,
                    email:data.email,
                    phone: data.phone
                };
 return collection.findAndModify(
          {_id:id}, // query
          [['_id','asc']],  // sort order
          {$set: userData}, // replacement
          { "new": true }
          ).then(function(doc) {
                if(!doc)
                    throw new Error('Record not updated.');
                return doc.value;   
          });
    }

module.exports = {
        update:update
}

1

これがE7 async / awaitの私の体験です。

async helperFunction()テストからが呼び出された場合...(ES7 asyncキーワードの1つの説明、つまり)

→確かに、あなたはそれを次のように呼びますawait helperFunction(whateverParams) (まあ、ええ、当然、知ったら...)

そして、それが機能するためには(「await is a reserved word」を回避するため)、テスト関数に外部非同期マーカーが必要です。

it('my test', async () => { ...

4
それをと呼ぶ必要はありませんawait helperFunction(...)asyncこの関数は約束を返します。asyncたまたまプロミスを返すマークされていない関数で行うのと同じように、返されたプロミスを処理できます。ポイントは、約束、期間を処理することです。機能のasync有無は問いません。約束を処理するための複数の方法の1つにawaitすぎません。
ルイ

1
そうだね。しかし、私はキャッチにラインを投資する必要があります...または私のテストは偽陽性として合格し、失敗した約束は(テストランナーの結果に関して)気付かれません。だから待って私にとってはライン&努力が少ないように見えます。–とにかく、待っていることを忘れていたのは、UnhandledPromiseRejectionWarning私にそれを引き起こした原因です...したがって、この答え。
フランクノッケ2017年

0

Chai-Webdriver for Seleniumでも同様の経験をしました。私awaitはアサーションに追加し、それが問題を修正しました:

Cucumberjsの使用例:

Then(/I see heading with the text of Tasks/, async function() {
    await chai.expect('h1').dom.to.contain.text('Tasks');
});

-7

この問題は、webpackをアンインストールした後で解決しました(react js problem)。

sudo uninstall webpack
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.