wake-up-neo.com

NodeJS UnhandledPromiseRejectionWarning

Ich teste also eine Komponente, die auf einem Event-Emitter basiert. Dafür habe ich eine Lösung mit Promises mit Mocha + Chai 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 ein "UnhandledPromiseRejectionWarning" angezeigt, obwohl die Ablehnungsfunktion aufgerufen wird, da sofort die Meldung "AssertionError: Promise error" angezeigt wird.

(node: 25754) UnhandledPromiseRejectionWarning: Unbearbeitetes Versprechen Ablehnung (Ablehnungs-ID: 2): AssertionError: Promise-Fehler: erwartet {Objekt (message, showDiff, ...)} um falsch zu sein 1) sollte mit dem richtigen Ereignis wechseln

Und dann, nach 2 Sekunden, bekomme ich 

Fehler: Timeout von 2000ms überschritten. Stellen Sie sicher, dass der done () - Rückruf .__ ist. in diesem Test aufgerufen werden.

Was noch seltsamer ist, seit der catch-Callback ausgeführt wurde (ich glaube, dass aus irgendeinem Grund der behauptete Fehler den Rest der Ausführung verhindert hat) 

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

97
Jzop

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 done() nie aufgerufen wird, da der Code vorher fehlerhaft war. Das ist es, was den Timeout verursacht.

Die "Unhandled-Versprechen-Ablehnung" wird auch durch die fehlgeschlagene Behauptung verursacht. Wenn in einem catch()-Handler ein Fehler ausgegeben wird und es keinen nachfolgenden catch()-Handler gibt, wird der Fehler verschluckt (als erklärt in diesem Artikel ). Die Warnung UnhandledPromiseRejectionWarning weist Sie auf diese Tatsache hin.

Wenn Sie versprechenbasierten Code in Mocha testen möchten, sollten Sie sich im Allgemeinen darauf verlassen, dass Mocha selbst bereits mit Versprechen umgehen kann. Sie sollten done() nicht verwenden, sondern ein Versprechen aus dem Test zurückgeben. Mocha fängt dann alle Fehler selbst auf.

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

Die Assertionsbibliotheken in Mocha funktionieren, indem sie einen Fehler auslösen, wenn die Assertion nicht korrekt war. Das Auslösen eines Fehlers führt zu einem abgelehnten Versprechen, selbst wenn es in der Executor-Funktion der catch-Methode ausgelöst wird.

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

Im obigen Code wertet die Variable error die Variable true aus, sodass die Assertionsbibliothek einen Fehler auslöst ... der niemals abgefangen wird. Aufgrund des Fehlers wird die done-Methode niemals aufgerufen. Mochas done-Callback akzeptiert diese Fehler. Sie können also alle Versprechungsketten in Mocha mit .then(done,done) beenden. Dadurch wird sichergestellt, dass die done-Methode immer aufgerufen wird und der Fehler auf dieselbe Weise gemeldet wird, als wenn Mocha den Fehler der Assertion 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 bezeichne diesen Artikel für die Idee der Verwendung von .then (done, done) beim Testen von Versprechen in Mocha.

8
Matthew Orlando

Ich habe diesen Fehler bekommen, als ich mit Sinon getreten bin.

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

Anstatt ...

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

Benutzen ...

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

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

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

8
danday74

Für diejenigen, die den Fehler/die Warnung UnhandledPromiseRejectionWarning außerhalb einer Testumgebung suchen, ist es wahrscheinlich, dass sich niemand im Code um den eventuellen Fehler 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!

und das Hinzufügen der .catch() oder die Behandlung des Fehlers sollte die Warnung/den Fehler beheben

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

Oder mit dem zweiten Parameter in der Funktion then

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

Hier ist meine Erfahrung mit E7 async/await :

Falls Sie eine async helperFunction() haben, die von Ihrem Test aufgerufen wurde ... (eine Erklärung mit dem Schlüsselwort ES7 async, meine ich)

→ Stellen Sie sicher, dass Sie das als await helperFunction(whateverParams) bezeichnen (na ja, natürlich, sobald Sie es wissen ...)

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

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

Ich habe mich dieser Ausgabe gestellt:

(node: 1131004) UnhandledPromiseRejectionWarning: Unbeaufsichtigte Zusage für die Zusage von Zusagen (bezüglich jection id: 1): TypeError: res.json ist keine Funktion. (Knoten: 1131004) DeprecationWarning: Abweisungen für unbehandelte Zusagen werden nicht mehr empfohlen . Zukünftige Ablehnungsversprechen, die nicht bearbeitet werden, beenden den Node.j s Prozess mit einem Exit-Code ungleich Null.

Es war mein Fehler, ich ersetzte res object in then(function(res), änderte also res in Ergebnis 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
}
0

Ich habe dieses Problem nach der Deinstallation von Webpack gelöst (reagiere auf js Problem).

Sudo uninstall webpack
0
Mr Fun