wake-up-neo.com

Node.js: Wann werden Promises vs Callbacks verwendet?

Ich habe älteren Node.js-Code, den ich aktualisiere. Dabei entwerfe ich neue Module, um mit dem alten Code zu arbeiten. Ich stelle fest, dass ich mich im Gegensatz zum ersten Schreiben mehr auf die Verwendung von ES6-Versprechungen als auf Rückrufe stütze. Nun habe ich diese Mischung aus einigen Funktionen, die Versprechen zurückgeben und einige Rückrufe annehmen - was mühsam ist. Ich denke, es sollte überarbeitet werden, um Versprechungen zu verwenden. Aber bevor das erledigt ist ... 

In welchen Situationen werden Versprechen bevorzugt und wo werden Rückrufe bevorzugt?

Gibt es eine Situation, die ein Rückruf besser bewältigen kann als ein Versprechen und umgekehrt?

Basierend auf dem, was ich bisher gesehen habe, sehe ich keinen Grund, Callbacks anstelle von Versprechen zu verwenden. Ist das wahr?

15
Sean Lynch

Zunächst einmal möchten Sie eigentlich nie Code schreiben, der eine Mischung aus Rückrufen und Versprechen für asynchrone Operationen ist. Wenn Sie zu Versprechungen wechseln oder einige Versprechen einführen, möchten Sie wahrscheinlich die Rückrufe in demselben Abschnitt des Codes in Versprechen umwandeln. Für die entsprechenden Arten von Operationen gibt es so viele Vorteile, dass Versprechungen im Vergleich zu einfachen Rückrufen so viele Vorteile bieten, dass sich die Konvertierung lohnt, wenn bereits in einem Code-Bereich gearbeitet wird.

Versprechen sind großartig für:

  • Überwachung synchroner Vorgänge
  • Das muss nur einmal gemeldet werden (in der Regel Abschluss oder Fehler)
  • Koordinieren oder Verwalten mehrerer asynchroner Vorgänge, z. B. Sequenzieren oder Verzweigen asynchroner Vorgänge oder gleichzeitiges Verwalten mehrerer Vorgänge im Flug
  • Fehler bei verschachtelten oder tief verschachtelten asynchronen Operationen
  • Code für die Verwendung von async/await vorbereiten (oder jetzt mit Transpiler verwenden)
  • Operationen, die in das Promise-Modell passen, in dem es nur drei Zustände gibt: pending, fulfilled und rejected und bei denen der Statusübergang von pending => fulfilled oder von pending => rejected nicht geändert werden kann (ein einzelner Einwegübergang).
  • Dynamische Verknüpfung oder Verkettung asynchroner Vorgänge (wie diese beiden asynchronen Vorgänge prüfen, das Ergebnis überprüfen und dann basierend auf dem Zwischenergebnis entscheiden, welche anderen asynchronen Vorgänge auszuführen sind)
  • Verwalten einer Mischung aus asynchronen und synchronen Vorgängen
  • Alle Ausnahmen, die in asynchronen Beendigungscallbacks auftreten, automatisch abfangen und nach oben ausbreiten (in einfachen Callbacks werden diese Ausnahmen manchmal unbemerkt verborgen).

Einfache Rückrufe sind für Dinge gut, die Versprechen nicht können:

  • Synchrone Benachrichtigungen (wie der Rückruf für Array.prototype.map())
  • Benachrichtigungen, die mehrmals auftreten können (und daher den Rückruf mehrmals aufrufen müssen). Versprechungen sind One-Shot-Geräte und können nicht für Wiederholungsbenachrichtigungen verwendet werden.
  • Situationen, die nicht in das ausstehende, erfüllte, abgelehnte Einwegzustandsmodell abgebildet werden können.

Und ich würde auch EventEmitter zum Mix hinzufügen.

EventEmitters eignen sich hervorragend für:

  • Benachrichtigungen zu Veröffentlichungs-/Abonnementtypen
  • Eine Schnittstelle mit einem Ereignismodell, insbesondere wenn Ereignisse mehrmals auftreten können (wie Streams)
  • Lose Kopplungen, wenn Code von Drittanbietern ohne API mehr als ein eventEmitter teilnehmen oder etwas überwachen soll. Keine zu entwickelnde API Machen Sie einfach einen eventEmitter öffentlich und definieren Sie einige Ereignisse und die dazugehörigen Daten.

Hinweise zum Konvertieren eines einfachen Rückrufcodes in Promises

Wenn Ihre Rückrufe in die Knotenaufrufkonvention passen, wobei der Rückruf als letztes Argument übergeben und wie callback(err, result) aufgerufen wird, wickeln Sie die übergeordnete Funktion mit util.promisify() in node.js oder mit der Bluebird Promise-Bibliothek mit Promise.promisify() .

Mit Bluebird können Sie sogar ein gesamtes Modul (das async-Callbacks in der node.js-Aufrufkonvention verwendet) auf einmal aktivieren, z.

const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));

fs.writeFileAsync("file.txt", data).then(() => {
    // done here
}).catch(err => {
    // error here
});

In node.js Version 8+

Es gibt jetzt util.promisify(), das eine async-Funktion, die die async-Aufrufkonvention von node.js verwendet, in eine Funktion konvertiert, die ein Versprechen zurückgibt.

Beispiel aus das Dokument:

const util = require('util');
const fs = require('fs');

const stat = util.promisify(fs.stat);

// usage of promisified function
stat('.').then((stats) => {
  // Do something with `stats`
}).catch((error) => {
  // Handle the error.
});
27
jfriend00

Sie existieren beide, um das gleiche Problem zu lösen, und behandeln das Ergebnis einer asynchronen Funktion. 

Callbacks sind in der Regel ausführlicher und das gleichzeitige Koordinieren mehrerer asynchroner Anforderungen kann zu callback hell führen, wenn Sie Ihre Funktionen nicht aktiv modularisieren. Fehlerbehandlung und -verfolgung sind in der Regel weniger unkompliziert und sogar verwirrend, da es viele Fehlerobjekte geben kann, die alle auf einen einzelnen Fehler zurückgehen, der sich im Aufrufstapel befindet. Fehler müssen auch an den ursprünglichen Aufrufer zurückgegeben werden. Dies kann auch zu Kopfschmerzen führen, wenn festgestellt wird, wo der ursprüngliche Fehler ausgegeben wurde, wenn anonyme Funktionen in der Rückrufkette verwendet wurden. Callbacks haben den Vorteil, dass es sich bei ihnen lediglich um alte Funktionen handelt, die kein weiteres Verständnis erfordern, wenn man nicht weiß, wie ein asynchroner Vorgang funktioniert.

Versprechungen sind häufiger, da sie weniger Code erfordern, lesbarer sind, da sie wie synchrone Funktionen geschrieben werden, einen einzigen Fehlerkanal haben, ausgelöste Fehler behandeln können und util.promisify() in der neuesten Version von Node.js hinzugefügt wird , kann Error-First Callbacks in Versprechen umwandeln. Es gibt auch async/await, das jetzt auch in Node.js ist, und sie sind auch mit Promises verbunden.

Dies ist absolut auf der Meinung nach basiert, also geht es wirklich darum, was Sie am besten können, aber Promises und async/await sind die Entwicklung des Rückrufs und verbessern die asynchrone Entwicklung. Dies ist keinesfalls ein erschöpfender Vergleich, sondern ein Blick auf Callbacks und Versprechen auf hoher Ebene.

4
peteb

Ich weiß nicht mehr, woher ich dieses Zeug bekommen habe, aber es könnte hilfreich sein, Versprechen besser zu verstehen.

Versprechen sind keine Rückrufe. Ein Versprechen stellt das zukünftige Ergebnis einer asynchronen Operation dar. Wenn Sie sie so schreiben, wie Sie es tun, haben Sie wenig Nutzen. Wenn Sie sie jedoch so schreiben, wie sie verwendet werden sollen, können Sie asynchronen Code auf eine Weise schreiben, die synchronem Code ähnelt und viel einfacher zu folgen ist: VORTEILE 1. Lesbarkeit über Rückrufe 2. Fehler leicht zu fangen .. 3. Gleichzeitige Rückrufe 

1. Lesbarkeit über Rückrufe Versprechungen bieten eine prägnantere und klarere Darstellung von sequentiellen asynchronen Vorgängen in Javascript. Sie sind praktisch eine andere Syntax, um denselben Effekt wie Callbacks zu erzielen. Der Vorteil ist eine erhöhte Lesbarkeit. Etwas wie das

aAsync()   
.then(bAsync)  
 .then(cAsync)   
.done(finish); 

ist viel lesbarer als das Äquivalent der Übergabe jeder dieser einzelnen Funktionen als Rückrufe, wie

Async(function(){     return bAsync(function(){         return cAsync(function(){             finish()         })     }) }); 
//-------------------------------------------- api().then(function(result){  
   return api2(); }).then(function(result2){     return api3(); }).then(function(result3){      // do work }); 

2. Einfach, Fehler zu fangen. Sicher nicht viel weniger Code, aber viel lesbarer. Dies ist jedoch nicht das Ende. Entdecken Sie die wahren Vorteile: Was wäre, wenn Sie in einem der Schritte nach Fehlern suchen wollten? Es wäre die Hölle, es mit Rückrufen zu tun, aber mit Versprechen ist es ein Kinderspiel: 

api().then(function(result){   
return api2(); }).then(function(result2){     return api3(); }).then(function(result3){      // do work }).catch(function(error) {    
  //handle any error that may occur before this point }); 
/* Pretty much the same as a try { ... } catch block. 
Even better: */
 api().then(function(result){     return api2(); }).then(function(result2){     return api3(); }).then(function(result3){      // do work }).catch(function(error) {      //handle any error that may occur before this point }).then(function() {      //do something whether there was an error or not      //like hiding an spinner if you were performing an AJAX request. });

3. Gleichzeitige Rückrufe Und noch besser: Was wäre, wenn diese 3 Aufrufe an api, api2, api3 gleichzeitig ablaufen könnten (z. B. wenn es sich um Aufrufe von AJAX handelt), aber Sie mussten auf die drei warten? Ohne Versprechen sollten Sie eine Art Zähler erstellen. Mit den Versprechen, die ES6-Notation zu verwenden, ist ein weiteres Stück Kuchen und ziemlich ordentlich:

Promise.all([api(), api2(), api3()]).then(function(result) {     //do work. result is an array contains the values of the three fulfilled promises. }).catch(function(error) {     //handle the error. At least one of the promises rejected. });

Ich hoffe, Sie sehen jetzt Versprechen in einem neuen Licht. 

1
Krishnadas PC