NodeJS UnhandledPromiseRejectionWarning

133

Ich teste also eine Komponente, die auf einem Ereignisemitter basiert. Zu diesem Zweck habe ich mit Promises with Mocha + Chai eine Lösung gefunden:

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

Auf der Konsole wird "UnhandledPromiseRejectionWarning" angezeigt, obwohl die Ablehnungsfunktion aufgerufen wird, da sofort die Meldung "AssertionError: Promise error" angezeigt wird.

(Knoten: 25754) UnhandledPromiseRejectionWarning: Nicht behandelte Ablehnung von Versprechungen (Ablehnungs-ID: 2): AssertionError: Versprechungsfehler: Es wird erwartet, dass {Objekt (Nachricht, showDiff, ...)} mit dem richtigen Ereignis übergeht

Und dann, nach 2 Sekunden bekomme ich

Fehler: Zeitüberschreitung von 2000 ms überschritten. Stellen Sie sicher, dass der Rückruf done () in diesem Test aufgerufen wird.

Was noch seltsamer ist, seit der Catch-Callback ausgeführt wurde (ich denke, dass aus irgendeinem Grund der Assert-Fehler den Rest der Ausführung verhindert hat).

Nun das Lustige, wenn ich das auskommentiere, assert.isNotOk(error...)läuft der Test ohne Warnung in der Konsole einwandfrei. Es schlägt immer noch in dem Sinne fehl, dass es den Fang ausführt.
Trotzdem kann ich diese Fehler nicht mit Versprechen verstehen. Kann mich jemand aufklären?

Jzop
quelle
Ich denke, Sie haben in der letzten Zeile einen zusätzlichen Satz Schließklammer und Parens. Bitte löschen Sie sie und versuchen Sie es erneut.
Reduzieren Sie den
4
Das ist so cool, dass die neue unbehandelte Ablehnungswarnung Fehler im wirklichen Leben findet und den Menschen Zeit spart. So viel gewinnen hier. Ohne diese Warnung wären Ihre Tests ohne Erklärung abgelaufen.
Benjamin Gruenbaum

Antworten:

161

Das Problem wird dadurch verursacht:

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

Wenn die Zusicherung fehlschlägt, wird ein Fehler ausgegeben. Dieser Fehler führt dazu, dass er done()niemals aufgerufen wird, da der Code zuvor fehlerhaft war. Das ist es, was das Timeout verursacht.

Die "Ablehnung eines nicht behandelten Versprechens" wird auch durch die fehlgeschlagene Behauptung verursacht. Wenn ein Fehler in einem catch()Handler ausgelöst wird und kein nachfolgender catch()Handler vorhanden ist , wird der Fehler verschluckt (wie in diesem Artikel erläutert ). Die UnhandledPromiseRejectionWarningWarnung macht Sie auf diese Tatsache aufmerksam.

Wenn Sie versprechungsbasierten Code in Mocha testen möchten, sollten Sie sich im Allgemeinen darauf verlassen, dass Mocha selbst bereits Versprechungen verarbeiten kann. Sie sollten nicht verwenden done(), sondern ein Versprechen von Ihrem Test zurückgeben. Mokka erkennt dann alle Fehler selbst.

So was:

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');
  });
});
Robertklep
quelle
7
Für alle Neugierigen gilt dies auch für Jasmin.
Nick Radford
@robertklep Wird nicht gefangen, wird ein Fehler angezeigt und nicht nur der, den Sie erwarten? Ich denke, dieser Stil wird nicht funktionieren, wenn Sie versuchen, Fehler zu behaupten.
TheCrazyProgrammer
1
@TheCrazyProgrammer Der catchHandler sollte wahrscheinlich als zweites Argument an übergeben werden then. Ich bin mir jedoch nicht ganz sicher, was die Absicht des OP war, also habe ich es so belassen, wie es ist.
Robertklep
1
Und auch für alle, die neugierig auf Jasmin sind, verwenden Sie done.fail('msg')in diesem Fall.
Paweł
Können Sie ein vollständiges Beispiel dafür geben, wohin der Hauptcode und die Behauptungen gehen sollen, was hier nicht so klar ist? Insbesondere, wenn Sie einen Wert aus dem ursprünglichen Service- / Versprechungsaufruf in unserem Code behaupten. Gehört das eigentliche Versprechen für unseren Code zu diesem anderen "Mokka-Versprechen"?
bjm88
10

Ich habe diesen Fehler beim Stubben mit Sinon erhalten.

Die Lösung besteht darin, das versprochene npm-Paket sinon zu verwenden , wenn Versprechen mit Stubs gelöst oder abgelehnt werden.

Anstatt ...

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

Verwenden ...

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

Es gibt auch eine Auflösungsmethode (beachten Sie das s am Ende).

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

danday74
quelle
1
Sinon enthält jetzt die Methoden "Auflösen" und " Zurückweisen " für Stubs ab Version 2. Siehe npmjs.com/package/sinon-as-promised . Ich habe immer noch die Antwort erhalten - ich wusste nichts davon.
Andrew
9

Die Assertionsbibliotheken in Mocha geben einen Fehler aus, wenn die Assertion nicht korrekt war. Das Auslösen eines Fehlers führt zu einem abgelehnten Versprechen, selbst wenn die für die catchMethode bereitgestellte Executor-Funktion ausgelöst wird.

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

Im obigen Code errorwertet das Objekt aus, truesodass die Assertionsbibliothek einen Fehler auslöst ... der niemals abgefangen wird. Aufgrund des Fehlers wird die doneMethode niemals aufgerufen. Mochas doneRückruf akzeptiert diese Fehler, sodass Sie einfach alle Versprechensketten in Mocha mit beenden können .then(done,done). Dies stellt sicher, dass die Methode done immer aufgerufen wird und der Fehler auf dieselbe Weise gemeldet wird, wie wenn Mocha den Fehler der Zusicherung im synchronen Code abfängt.

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

Ich gebe diesem Artikel Anerkennung für die Idee, .then (erledigt, erledigt) beim Testen von Versprechungen in Mokka zu verwenden.

Matthew Orlando
quelle
6

Für diejenigen, die UnhandledPromiseRejectionWarningaußerhalb einer Testumgebung nach dem Fehler / der Warnung suchen , könnte dies wahrscheinlich daran liegen, dass sich niemand im Code um den eventuellen Fehler in einem Versprechen kümmert:

In diesem Code wird beispielsweise die in dieser Frage gemeldete Warnung angezeigt:

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

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

Das Hinzufügen .catch()oder Behandeln des Fehlers sollte die Warnung / den Fehler beheben

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

Oder verwenden Sie den zweiten Parameter in der thenFunktion

new Promise((resolve, reject) => {
  return reject('Error reason!');
}).then(null, () => { /* do whatever you want here */ });
gsalgadotoledo
quelle
1
Natürlich, aber ich denke, im wirklichen Leben verwenden wir normalerweise nicht nur, new Promise((resolve, reject) => { return reject('Error reason!'); })sondern in der Funktion, function test() { return new Promise((resolve, reject) => { return reject('Error reason!'); });}sodass wir innerhalb der Funktion keine verwenden müssen, .catch()aber um Fehler erfolgreich zu behandeln, reicht es aus, diese Funktion aufzurufen test().catch(e => console.log(e))oder die asynchrone / try { await test() } catch (e) { console.log(e) }
wartende
1

Ich stand vor diesem Problem:

(Knoten: 1131004) UnhandledPromiseRejectionWarning: Nicht behandelte Ablehnung von Versprechen (Ablehnungs-ID: 1): TypeError: res.json ist keine Funktion (Knoten: 1131004) DeprecationWarning: Nicht behandelte Ablehnungen von Versprechen sind veraltet. In Zukunft beenden Versprechen, die nicht behandelt werden, den Prozess von Node.j mit einem Exit-Code ungleich Null.

Es war mein Fehler, ich habe das resObjekt ersetzt then(function(res), also resin Ergebnis geändert und jetzt funktioniert es.

Falsch

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

Korrektur

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

Service code:

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
}
Muhammad Shahzad
quelle
1

Hier ist meine Erfahrung mit E7 async / await :

Falls Sie async helperFunction()einen Anruf von Ihrem Test haben ... (eine Erklärung mit dem ES7- asyncSchlüsselwort, meine ich)

→ stellen Sie sicher, dass Sie das so nennen await helperFunction(whateverParams) (na ja, natürlich, sobald Sie es wissen ...)

Und damit dies funktioniert (um zu vermeiden, dass Warten ein reserviertes Wort ist), muss Ihre Testfunktion einen äußeren asynchronen Marker haben:

it('my test', async () => { ...
Frank Nocke
quelle
4
Sie müssen es nicht so nennen await helperFunction(...). Eine asyncFunktion gibt ein Versprechen zurück. Sie können das zurückgegebene Versprechen einfach so behandeln, wie Sie es bei einer nicht markierten Funktion tun würden async, die zufällig ein Versprechen zurückgibt. Es geht darum, das Versprechen zu erfüllen, Punkt. Ob die Funktion ist asyncoder nicht, spielt keine Rolle. awaitist nur eine von mehreren Möglichkeiten, mit dem Versprechen umzugehen.
Louis
1
Wahr. Aber dann muss ich Linien in das Fangen investieren ... oder meine Tests werden als falsch positiv eingestuft, und alle fehlgeschlagenen Versprechen bleiben unbemerkt (in Bezug auf die Testergebnisse). Das Warten sieht für mich nach weniger Zeilen und Aufwand aus. - Wie auch immer, das Warten zu vergessen war, was das UnhandledPromiseRejectionWarningfür mich verursacht hat ... also diese Antwort.
Frank Nocke
0

Ich hatte eine ähnliche Erfahrung mit Chai-Webdriver für Selen. Ich awaithabe der Behauptung hinzugefügt und das Problem behoben:

Beispiel mit Cucumberjs:

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

Ich habe dieses Problem nach der Deinstallation von Webpack gelöst (reagieren Sie auf das Problem).

sudo uninstall webpack
Herr Spaß
quelle