875 Stimmen

Wie konvertiere ich eine vorhandene Rückruf-API in Promises?

Ich möchte mit Promises arbeiten, habe aber eine Callback-API in einem Format wie:

1. DOM load oder andere einmalige Ereignisse:

window.onload; // auf callback setzen
...
window.onload = function() {

};

2. Einfacher Callback:

function request(onChangeHandler) {
    ...
}
request(function() {
    // Änderung erfolgte
    ...
});

3. Node-Style-Callback ("nodeback"):

function getStuff(dat, callback) {
    ...
}
getStuff("dataParam", function(err, data) {
    ...
})

4. Eine ganze Bibliothek mit Node-Style-Callbacks:

API;
API.one(function(err, data) {
    API.two(function(err, data2) {
        API.three(function(err, data3) {
            ...
        });
    });
});

Wie arbeite ich mit der API mit Promises, wie 'promisify' ich sie?

872voto

Benjamin Gruenbaum Punkte 259076

Versprechen haben einen Zustand, sie beginnen als ausstehend und können sich auf folgendes einstellen:

  • erfüllt bedeutet, dass die Berechnung erfolgreich abgeschlossen wurde.
  • abgelehnt bedeutet, dass die Berechnung fehlgeschlagen ist.

Versprechen zurückgebende Funktionen sollten nie Ausnahmen auslösen, stattdessen sollten sie Ablehnungen zurückgeben. Das Werfen aus einer Funktion, die Versprechen zurückgibt, zwingt Sie dazu, sowohl ein } catch { als auch ein .catch zu verwenden. Personen, die promisifizierte APIs verwenden, erwarten nicht, dass Versprechen Ausnahmen auslösen. Wenn Sie sich nicht sicher sind, wie asynchrone APIs in JS funktionieren - sehen Sie sich bitte zuerst diese Antwort an.

1. DOM-Load oder andere einmalige Ereignisse:

Das Erstellen von Versprechen bedeutet im Allgemeinen, anzugeben, wann sie aufgelöst werden - das bedeutet, wann sie in die erfüllte oder abgelehnte Phase übergehen, um anzuzeigen, dass die Daten verfügbar sind (und mit .then abgerufen werden können).

Mit modernen Versprechen-Implementierungen, die den Promise-Konstruktor unterstützen, wie nativen ES6-Versprechen:

function load() {
    return new Promise(function(resolve, reject) {
        window.onload = resolve;
    });
}

Sie würden das resultierende Versprechen dann wie folgt verwenden:

load().then(function() {
    // Dinge nach dem Laden ausführen
});

Mit Bibliotheken, die aufgeschobene (defered) unterstützen (Lassen Sie uns hier $q für dieses Beispiel verwenden, aber später verwenden wir auch jQuery):

function load() {
    var d = $q.defer();
    window.onload = function() { d.resolve(); };
    return d.promise;
}

Oder mit einer jQuery-ähnlichen API, die einmalig auftretende Ereignisse erfasst:

function done() {
    var d = $.Deferred();
    $("#myObject").once("click",function() {
        d.resolve();
    });
    return d.promise();
}

2. Einfacher Rückruf (Callback):

Diese APIs sind ziemlich verbreitet, da Rückrufe in JS häufig vorkommen. Schauen wir uns den häufigen Fall an, dass onSuccess und onFail vorhanden sind:

function getUserData(userId, onLoad, onFail) { …

Mit modernen Versprechen-Implementierungen, die den Promise-Konstruktor unterstützen, wie nativen ES6-Versprechen:

function getUserDataAsync(userId) {
    return new Promise(function(resolve, reject) {
        getUserData(userId, resolve, reject);
    });
}

Mit Bibliotheken, die aufgeschobene unterstützen (Lassen Sie uns hier für dieses Beispiel jQuery verwenden, aber oben haben wir auch $q verwendet):

function getUserDataAsync(userId) {
    var d = $.Deferred();
    getUserData(userId, function(res){ d.resolve(res); }, function(err){ d.reject(err); });
    return d.promise();
}

JQuery bietet auch eine $.Deferred(fn)-Form, die den Vorteil bietet, dass wir einen Ausdruck schreiben können, der der new Promise(fn)-Form sehr nahe kommt, wie folgt:

function getUserDataAsync(userId) {
    return $.Deferred(function(dfrd) {
        getUserData(userId, dfrd.resolve, dfrd.reject);
    }).promise();
}

Hinweis: Hier nutzen wir die Tatsache aus, dass die resolve und reject Methoden eines jQuery-Deffered "abtrennbar" sind; d.h. sie sind an die Instanz eines jQuery.Deferred() gebunden. Nicht alle Bibliotheken bieten dieses Feature.

3. Node-Stil-Rückruf ("Nodeback"):

Node-Stil-Rückrufe (Nodebacks) haben ein spezielles Format, bei dem der Rückruf immer das letzte Argument ist und sein erster Parameter ein Fehler ist. Lassen Sie uns zuerst manuell eines davon zu einem Versprechen machen:

getStuff("dataParam", function(err, data) { …

Zu:

function getStuffAsync(param) {
    return new Promise(function(resolve, reject) {
        getStuff(param, function(err, data) {
            if (err !== null) reject(err);
            else resolve(data);
        });
    });
}

Mit aufgeschobenen (deferreds) können Sie folgendes tun (Lassen Sie uns hier Q für dieses Beispiel verwenden, obwohl Q jetzt die neue Syntax unterstützt die Sie bevorzugen sollten):

function getStuffAsync(param) {
    var d = Q.defer();
    getStuff(param, function(err, data) {
        if (err !== null) d.reject(err);
        else d.resolve(data);
    });
    return d.promise;   
}

Im Allgemeinen sollten Sie Dinge nicht zu sehr manuell verabeiten, die meisten Versprechen-Bibliotheken, die auch für Node gedacht waren, sowie native Versprechen in Node 8+ haben eine integrierte Methode zum Umwandeln von Nodebacks in Versprechen. Zum Beispiel

var getStuffAsync = Promise.promisify(getStuff); // Bluebird
var getStuffAsync = Q.denodeify(getStuff); // Q
var getStuffAsync = util.promisify(getStuff); // Native Versprechen, nur Node

4. Eine ganze Bibliothek mit Node-Stil-Rückrufen:

Es gibt keine golden Regel hier, Sie promisifizieren sie einzeln. Einige Versprechen-Implementierungen erlauben es jedoch, dies in einem Stapel durchzuführen, zum Beispiel in Bluebird, das Konvertieren einer Nodeback-API in eine Promise-API ist so einfach wie:

Promise.promisifyAll(API);

Oder mit nativen Versprechen in Node:

const { promisify } = require('util');
const promiseAPI = Object.entries(API).map(([key, v]) => ({key, fn: promisify(v)}))
                         .reduce((o, p) => Object.assign(o, {[p.key]: p.fn}), {});

Notizen:

  • Natürlich benötigen Sie in einem .then-Handler keine Promisifizierung. Das Zurückgeben eines Versprechens aus einem .then-Handler wird mit dem Wert dieses Versprechens erfüllt oder abgelehnt. Das Werfen aus einem .then-Handler ist ebenfalls eine gute Praxis und wird das Versprechen ablehnen - dies ist die berühmte Sicherheit bei der Promise-Werfung.
  • In einem tatsächlichen onload-Fall sollten Sie addEventListener anstelle von onX verwenden.

66voto

efkan Punkte 12166

Heute kann ich Promise in Node.js als eine einfache Javascript-Methode verwenden.

Ein einfaches und grundlegendes Beispiel für Promise (auf KISS-weise):

Einfacher Javascript Async API-Code:

function divisionAPI (number, divider, successCallback, errorCallback) {

    if (divider == 0) {
        return errorCallback( new Error("Division durch Null") )
    }

    successCallback( number / divider )

}

Promise Javascript Async API-Code:

function divisionAPI (number, divider) {

    return new Promise(function (fulfilled, rejected) {

        if (divider == 0) {
            return rejected( new Error("Division durch Null") )
        }

        fulfilled( number / divider )

     })

}

(Ich empfehle den Besuch dieser schönen Quelle: diese schöne Quelle)

Auch Promise kann zusammen mit async/await in ES7 verwendet werden, um den Programmfluss auf ein erfülltes Ergebnis warten zu lassen, wie folgt:

function getName () {

    return new Promise(function (fulfilled, rejected) {

        var name = "John Doe";

        // Warten Sie vor dem Aufruf der fulfilled() Methode 3000 Millisekunden
        setTimeout ( 
            function() {
                fulfilled( name )
            }, 
            3000
        )

    })

}

async function foo () {

    var name = await getName(); // wartet auf ein erfülltes Ergebnis!

    console.log(name); // Die Konsole schreibt "John Doe" nach 3000 Millisekunden

}

foo() // Aufruf der foo() Methode, um den Code auszuführen

Weitere Verwendung des gleichen Codes unter Verwendung der .then() Methode

function getName () {

    return new Promise(function (fulfilled, rejected) {

        var name = "John Doe";

        // Warten Sie vor dem Aufruf der fulfilled() Methode 3000 Millisekunden
        setTimeout ( 
            function() {
                fulfilled( name )
            }, 
            3000
        )

    })

}

// Die Konsole schreibt "John Doe" nach 3000 Millisekunden
getName().then(function(name){ console.log(name) })

Promise kann auch auf jeder Plattform, die auf Node.js basiert, wie z. B. react-native, verwendet werden.

Bonus: Eine hybride Methode
(Die Callback-Methode wird angenommen, dass sie zwei Parameter als Fehler und Ergebnis hat)

function divisionAPI (number, divider, callback) {

    return new Promise(function (fulfilled, rejected) {

        if (divider == 0) {
            let error = new Error("Division durch Null")
            callback && callback( error )
            return rejected( error )
        }

        let result = number / divider
        callback && callback( null, result )
        fulfilled( result )

     })

}

Die obige Methode kann Ergebnisse für altmodische Callbacks und Promise-Verwendungen liefern.

Hoffentlich hilft dies.

41voto

Siva Kannan Punkte 1968

Vor der Umwandlung einer Funktion als Promise in Node.JS

var request = require('request'); // http wrappedes Modul

function requestWrapper(url, callback) {
    request.get(url, function (err, response) {
      if (err) {
        callback(err);
      }else{
        callback(null, response);             
      }      
    })
}

requestWrapper(url, function (err, response) {
    console.log(err, response)
})

Nach der Umwandlung

var request = require('request');

function requestWrapper(url) {
  return new Promise(function (resolve, reject) { // promise zurückgeben
    request.get(url, function (err, response) {
      if (err) {
        reject(err); // Promise ablehnen
      }else{
        resolve(response); // Promise auflösen
      }
    })
  })
}

requestWrapper('http://localhost:8080/promise_request/1').then(function(response){
    console.log(response) // Erfolgreiche Auflösungsrückruf
}).catch(function(error){
    console.log(error) // Ablehnungsrückruf
})

Falls Sie mehrere Anfragen behandeln müssen

var allRequests = [];
allRequests.push(requestWrapper('http://localhost:8080/promise_request/1')) 
allRequests.push(requestWrapper('http://localhost:8080/promise_request/2'))
allRequests.push(requestWrapper('http://localhost:8080/promise_request/5'))    

Promise.all(allRequests).then(function (results) {
  console.log(results);// das Ergebnis wird ein Array sein, das jede Promise-Antwort enthält
}).catch(function (err) {
  console.log(err)
});

29voto

Gian Marco Punkte 20125

Node.js 8.0.0 enthält eine neue util.promisify() API, die es ermöglicht, Standard-Node.js-Callback-Style-APIs in eine Funktion zu verpacken, die ein Promise zurückgibt. Ein Beispiel für die Verwendung von util.promisify() wird unten gezeigt.

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

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

readFile('/some/file')
  .then((data) => { /* ... */ })
  .catch((err) => { /* ... */ });

Siehe Verbesserte Unterstützung für Promises

26voto

Leo Punkte 3776

Ich glaube nicht, dass der Vorschlag von @Benjamin mit window.onload immer funktionieren wird, da er nicht erkennt, ob er nach dem Laden aufgerufen wird. Das hat mich schon oft getroffen. Hier ist eine Version, die immer funktionieren sollte:

function promiseDOMready() {
    return new Promise(function(resolve) {
        if (document.readyState === "complete") return resolve();
        document.addEventListener("DOMContentLoaded", resolve);
    });
}
promiseDOMready().then(initOnLoad);

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X