webentwicklung-frage-antwort-db.com.de

Sind Versprechen nicht nur Rückrufe?

Ich entwickle seit ein paar Jahren JavaScript und verstehe die Aufregung um Versprechen überhaupt nicht.

Alles, was ich tue, scheint sich zu ändern:

api(function(result){
    api2(function(result2){
        api3(function(result3){
             // do work
        });
    });
});

Wofür ich sowieso eine Bibliothek wie async verwenden könnte, mit so etwas wie:

api().then(function(result){
     api2().then(function(result2){
          api3().then(function(result3){
               // do work
          });
     });
});

Welches ist mehr Code und weniger lesbar. Ich habe hier nichts gewonnen, es ist auch nicht plötzlich magisch "flach". Ganz zu schweigen davon, Dinge in Versprechen umwandeln zu müssen.

Also, was ist die große Aufregung um Versprechen hier?

391

Versprechen sind keine Rückrufe. Ein Versprechen repräsentiert das zukünftiges Ergebnis einer asynchronen Operation. Wenn Sie sie so schreiben, wie Sie es tun, erhalten Sie natürlich wenig Nutzen. Wenn Sie sie jedoch so schreiben, wie sie verwendet werden sollen, können Sie asynchronen Code so schreiben, dass er dem synchronen Code ähnelt und viel einfacher zu befolgen ist:

api().then(function(result){
    return api2();
}).then(function(result2){
    return api3();
}).then(function(result3){
     // do work
});

Sicherlich nicht viel weniger Code, aber viel besser lesbar.

Dies ist jedoch nicht das Ende. Lassen Sie uns die wahren Vorteile entdecken: Was ist, wenn Sie in einem der Schritte nach Fehlern suchen möchten? Es wäre die Hölle, dies mit Rückrufen zu tun, aber mit Versprechungen ist 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
});

So ziemlich das gleiche wie ein try { ... } catch Block.

Noch besser:

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

Und noch besser: Was ist, wenn diese 3 Aufrufe an api, api2, api3 könnte gleichzeitig ausgeführt werden (z. B. wenn AJAX Calls) waren, aber Sie mussten auf die drei warten? Ohne Versprechungen sollten Sie eine Art Zähler erstellen müssen. Mit Versprechungen, mit dem ES6 Notation, 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 Versprechen jetzt in einem neuen Licht.

582
Oscar Paz

Ja, Versprechen sind asynchrone Rückrufe. Sie können nichts tun, was Callbacks nicht können, und bei der Asynchronität treten dieselben Probleme auf wie bei normalen Callbacks.

Versprechen sind jedoch mehr als nur Rückrufe. Sie sind eine sehr mächtige Abstraktion, ermöglichen saubereren und besseren Funktionscode mit weniger fehleranfälliger Kesselplatte.

Also, was ist die Hauptidee?

Versprechen sind Objekte, die das Ergebnis einer einzelnen (asynchronen) Berechnung darstellen. Sie lösen zu diesem Ergebnis nur einmal auf. Das bedeutet ein paar Dinge:

Versprechen implementieren ein Beobachtermuster:

  • Sie müssen die Rückrufe, die den Wert verwenden, nicht kennen, bevor die Aufgabe abgeschlossen wird.
  • Anstatt Rückrufe als Argumente für Ihre Funktionen zu erwarten, können Sie ganz einfach ein Promise-Objekt return
  • Das Versprechen speichert den Wert und Sie können transparent einen Rückruf hinzufügen, wann immer Sie möchten. Es wird aufgerufen, wenn das Ergebnis verfügbar ist. "Transparenz" bedeutet, dass, wenn Sie ein Versprechen haben und einen Rückruf hinzufügen, es für Ihren Code keinen Unterschied macht, ob das Ergebnis noch vorliegt - API und Verträge sind identisch, was das Zwischenspeichern/Merken erheblich vereinfacht.
  • Sie können problemlos mehrere Rückrufe hinzufügen

Versprechen sind verkettbar ( monadisch , wenn du willst ):

  • Wenn Sie den Wert, den ein Versprechen darstellt, transformieren müssen, ordnen Sie dem Versprechen eine Transformationsfunktion zu und erhalten ein neues Versprechen zurück, das das transformierte Ergebnis darstellt. Sie können den Wert nicht synchron abrufen, um ihn irgendwie zu verwenden, aber Sie können die Transformation im Versprechen-Kontext einfach aufheben . Keine Boilerplate-Rückrufe.
  • Wenn Sie zwei asynchrone Tasks verketten möchten, können Sie die Methode .then() verwenden. Es wird ein Rückruf benötigt, um mit dem ersten Ergebnis aufgerufen zu werden, und es wird eine Zusage für das Ergebnis der Zusage zurückgegeben, dass der Rückruf zurückgegeben wird.

Hört sich kompliziert an? Zeit für ein Codebeispiel.

var p1 = api1(); // returning a promise
var p3 = p1.then(function(api1Result) {
    var p2 = api2(); // returning a promise
    return p2; // The result of p2 …
}); // … becomes the result of p3

// So it does not make a difference whether you write
api1().then(function(api1Result) {
    return api2().then(console.log)
})
// or the flattened version
api1().then(function(api1Result) {
    return api2();
}).then(console.log)

Die Abflachung ist nicht magisch, aber Sie können es leicht tun. Für Ihr stark verschachteltes Beispiel wäre das (nahe) Äquivalent

api1().then(api2).then(api3).then(/* do-work-callback */);

Wenn das Lesen des Codes dieser Methoden zum Verständnis beiträgt, hier ein grundlegendes Versprechen lib in wenigen Zeilen .

Was ist die große Aufregung um Versprechen?

Die Promise-Abstraktion ermöglicht eine wesentlich bessere Zusammenstellung von Funktionen. Beispielsweise erstellt die Funktion then neben all für die Verkettung ein Versprechen für das kombinierte Ergebnis mehrerer parallel wartender Versprechen.

Last but not least gibt es Versprechungen mit integrierter Fehlerbehandlung. Das Ergebnis der Berechnung kann sein, dass entweder das Versprechen mit einem Wert erfüllt wird oder dass es abgelehnt wird mit einem Grund. Alle Kompositionsfunktionen behandeln dies automatisch und verbreiten Fehler in Versprechensketten, so dass Sie sich nicht überall explizit darum kümmern müssen - im Gegensatz zu einer Implementierung mit einfachem Rückruf. Am Ende können Sie für alle aufgetretenen Ausnahmen einen dedizierten Fehler-Callback hinzufügen.

Ganz zu schweigen davon, Dinge in Versprechen umwandeln zu müssen.

Das ist bei guten Versprechungsbibliotheken eigentlich ziemlich trivial, siehe Wie konvertiere ich eine vorhandene Callback-API in Versprechungen?

157
Bergi

Zusätzlich zu den bereits etablierten Antworten verwandeln sich mit ES6-Pfeilfunktionen Versprechungen von einem bescheiden leuchtenden kleinen blauen Zwerg geradeaus in einen roten Riesen. Das ist kurz vor dem Zusammenbruch in eine Supernova:

api().then(result => api2()).then(result2 => api3()).then(result3 => console.log(result3))

Wie oligofren betonte, benötigen Sie ohne Argumente zwischen den API-Aufrufen überhaupt keine anonymen Wrapper-Funktionen:

api().then(api2).then(api3).then(r3 => console.log(r3))

Und schließlich, wenn Sie ein supermassives Schwarzes Loch erreichen möchten, können Versprechen erwartet werden:

async function callApis() {
    let api1Result = await api();
    let api2Result = await api2(api1Result);
    let api3Result = await api3(api2Result);

    return api3Result;
}
18
John Weisz

Zusätzlich zu den anderen Antworten lässt sich die ES2015-Syntax nahtlos mit den Versprechungen kombinieren und reduziert so noch mehr Boilerplate-Code:

// Sequentially:
api1()
  .then(r1 => api2(r1))
  .then(r2 => api3(r2))
  .then(r3 => {
      // Done
  });

// Parallel:
Promise.all([
    api1(),
    api2(),
    api3()
]).then(([r1, r2, r3]) => {
    // Done
});
11
Duncan Luk

Zusätzlich zu den fantastischen Antworten oben können 2 weitere Punkte hinzugefügt werden:

1. Semantischer Unterschied:

Versprechungen können bereits bei der Erstellung gelöst werden. Das heißt sie garantieren eher Bedingungen als Ereignisse. Wenn sie bereits aufgelöst sind, wird die übergebene aufgelöste Funktion weiterhin aufgerufen.

Umgekehrt behandeln Rückrufe Ereignisse. Wenn das Ereignis, an dem Sie interessiert sind, vor der Registrierung des Rückrufs stattgefunden hat, wird der Rückruf nicht aufgerufen.

2. Umkehrung der Kontrolle

Rückrufe beinhalten die Umkehrung der Kontrolle. Wenn Sie eine Rückruffunktion bei einer API registrieren, speichert die Javascript-Laufzeit die Rückruffunktion und ruft sie aus der Ereignisschleife auf, sobald sie zur Ausführung bereit ist.

Eine Erklärung finden Sie unter Die Javascript-Ereignisschleife .

Mit Promises liegt die Steuerung beim aufrufenden Programm. Die .then () -Methode kann jederzeit aufgerufen werden wenn wir das Versprechungsobjekt speichern.

9
dww

Versprechen sind keine Rückrufe, beide sind Programmiersprachen, die das asynchrone Programmieren erleichtern. Die Verwendung eines asynchronen/Warte-Programmierstils mit Coroutinen oder Generatoren, die Versprechungen zurückgeben, könnte als ein drittes solches Idiom angesehen werden. Ein Vergleich dieser Redewendungen für verschiedene Programmiersprachen (einschließlich Javascript) ist hier: https://github.com/KjellSchubert/promise-future-task

4
Kjell Schubert

Keine Versprechen sind nur Deckmantel für Rückrufe

beispiel Sie können Javascript native Versprechen mit Knoten js verwenden

my cloud 9 code link : https://ide.c9.io/adx2803/native-promises-in-node

/**
* Created by dixit-lab on 20/6/16.
*/

var express = require('express');
var request = require('request');   //Simplified HTTP request client.


var app = express();

function promisify(url) {
    return new Promise(function (resolve, reject) {
    request.get(url, function (error, response, body) {
    if (!error && response.statusCode == 200) {
        resolve(body);
    }
    else {
        reject(error);
    }
    })
    });
}

//get all the albums of a user who have posted post 100
app.get('/listAlbums', function (req, res) {
//get the post with post id 100
promisify('http://jsonplaceholder.typicode.com/posts/100').then(function (result) {
var obj = JSON.parse(result);
return promisify('http://jsonplaceholder.typicode.com/users/' + obj.userId + '/albums')
})
.catch(function (e) {
    console.log(e);
})
.then(function (result) {
    res.end(result);
}
)

})


var server = app.listen(8081, function () {

var Host = server.address().address
var port = server.address().port

console.log("Example app listening at http://%s:%s", Host, port)

})


//run webservice on browser : http://localhost:8081/listAlbums
1
Apoorv

Nein überhaupt nicht.

Callbacks sind einfach Funktionen in JavaScript , die aufgerufen und ausgeführt werden sollen, nachdem die Ausführung einer anderen Funktion beendet ist. Also wie passiert es?

Tatsächlich werden in JavaScript Funktionen selbst als Objekte betrachtet, und daher können wie alle anderen Objekte auch Funktionen als Argumente an andere Funktionen gesendet werden. Der gebräuchlichste und allgemeinste Anwendungsfall ist die Funktion setTimeout () in JavaScript.

Versprechen sind nichts anderes als ein viel improvisierterer Ansatz zum Behandeln und Strukturieren von asynchronem Code als dasselbe mit Rückrufen zu tun.

Das Promise erhält zwei Rückrufe in der Konstruktorfunktion: Auflösen und Zurückweisen. Diese Rückrufe innerhalb von Versprechungen ermöglichen uns eine genaue Kontrolle über die Fehlerbehandlung und Erfolgsfälle. Der Rückruf zum Auflösen wird verwendet, wenn die Ausführung des Versprechens erfolgreich war, und der Rückruf zum Ablehnen wird verwendet, um die Fehlerfälle zu behandeln.

0
Ayush Jain