Wie konvertiere ich eine vorhandene Callback-API in Promises?

Lesezeit: 13 Minuten

Wie konvertiere ich eine vorhandene Callback API in Promises
Benjamin Grünbaum

Ich möchte mit Versprechungen arbeiten, aber ich habe eine Rückruf-API in einem Format wie:

1. DOM-Laden oder anderes einmaliges Ereignis:

window.onload; // set to callback
...
window.onload = function() {

};

2. Einfacher Rückruf:

function request(onChangeHandler) {
    ...
}
request(function() {
    // change happened
    ...
});

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 in Versprechungen, wie „verspreche“ ich sie?

  • Ich habe meine eigene Antwort gepostet, aber Antworten, die erläutern, wie dies für eine bestimmte Bibliothek oder unter anderen Umständen geht, und auch Änderungen sind sehr willkommen.

    – Benjamin Grünbaum

    19. März 2014 um 22:49 Uhr

  • @Bergi Das ist eine interessante Idee, ich habe versucht, eine allgemeine Antwort zu geben, die die beiden gängigen Ansätze (Promise-Konstruktor und zurückgestelltes Objekt) verwendet. Ich habe versucht, die beiden Alternativen in Antworten zu geben. Ich stimme zu, dass RTFMing dieses Problem löst, aber wir stoßen oft sowohl hier als auch im Bugtracker auf dieses Problem, also dachte ich, dass eine “kanonische Frage” vorhanden ist – ich denke, RTFMing löst etwa 50% der Probleme im JS-Tag: D If Sie haben einen interessanten Einblick, den Sie in einer Antwort beitragen oder bearbeiten können. Dies wäre sehr willkommen.

    – Benjamin Grünbaum

    20. März 2014 um 13:24 Uhr

  • Hat das Erstellen von a new Promise einen erheblichen Overhead hinzufügen? Ich möchte alle meine synchronen Noje.js-Funktionen in ein Promise packen, um den gesamten synchronen Code aus meiner Node-App zu entfernen, aber ist dies die beste Vorgehensweise? Mit anderen Worten, eine Funktion, die ein statisches Argument (z. B. eine Zeichenfolge) akzeptiert und ein berechnetes Ergebnis zurückgibt, sollte ich das in ein Versprechen verpacken? …Ich habe irgendwo gelesen, dass Sie keinen synchronen Code in Nodejs haben sollten.

    – Ronnie Royston

    21. Januar 2019 um 19:12 Uhr


  • @RonRoyston nein, es ist keine gute Idee, synchrone Aufrufe mit Versprechen zu umschließen – nur asynchrone Aufrufe, die möglicherweise E / A ausführen

    – Benjamin Grünbaum

    22. Januar 2019 um 15:12 Uhr

Wie konvertiere ich eine vorhandene Callback API in Promises
Benjamin Grünbaum

Versprechen haben einen Status, sie beginnen als ausstehend und können sich wie folgt begleichen:

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

Versprechen Sie wiederkehrende Funktionen sollte niemals werfen, sollten sie stattdessen Ablehnungen zurückgeben. Das Auslösen einer Versprechen-Rückgabefunktion zwingt Sie dazu, sowohl a zu verwenden } catch { und ein .catch. Personen, die versprochene APIs verwenden, erwarten nicht, dass Versprechen geworfen werden. Wenn Sie nicht sicher sind, wie asynchrone APIs in JS funktionieren, sehen Sie sich bitte zuerst diese Antwort an.

1. DOM-Laden oder anderes einmaliges Ereignis:

Das Erstellen von Zusagen bedeutet also im Allgemeinen, anzugeben, wann sie abgerechnet werden – das heißt, wann sie in die Phase „Erfüllt“ oder „Abgelehnt“ wechseln, um anzuzeigen, dass die Daten verfügbar sind (und mit ihnen zugegriffen werden kann). .then).

Mit modernen Promise-Implementierungen, die das unterstützen Promise Konstruktor wie native ES6 verspricht:

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

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

load().then(function() {
    // Do things after onload
});

Mit Bibliotheken, die Deferred unterstützen (verwenden wir $q für dieses Beispiel hier, aber wir werden später auch jQuery verwenden):

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

Oder mit einer jQuery-ähnlichen API, die sich auf ein Ereignis einklinkt, das einmal passiert:

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

2. Einfacher Rückruf:

Diese APIs sind ziemlich gebräuchlich, da Callbacks in JS üblich sind. Schauen wir uns den üblichen Fall des Habens an onSuccess und onFail:

function getUserData(userId, onLoad, onFail) { …

Mit modernen Promise-Implementierungen, die das unterstützen Promise Konstruktor wie native ES6 verspricht:

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

Mit Bibliotheken, die Deferred unterstützen (verwenden wir für dieses Beispiel hier jQuery, aber wir haben oben 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 hat, dass wir einen Ausdruck schreiben können, der sehr genau nachahmt new Promise(fn) Form, wie folgt:

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

Hinweis: Hier nutzen wir die Tatsache aus, dass eine jQuery deferred ist resolve und reject Methoden sind “abnehmbar”; dh. sie sind an die gebunden Beispiel einer jQuery.Deferred(). Nicht alle Bibliotheken bieten diese Funktion.

3. Node-Style-Callback (“Nodeback”):

Rückrufe im Knotenstil (Nodebacks) haben ein bestimmtes Format, bei dem der Rückruf immer das letzte Argument ist und sein erster Parameter ein Fehler ist. Lassen Sie uns zuerst einen manuell versprechen:

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 Verzögerungen können Sie Folgendes tun (lassen Sie uns für dieses Beispiel Q 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 die Dinge nicht zu sehr manuell versprechen, die meisten Versprechungsbibliotheken, die für Node entwickelt wurden, sowie native Versprechungen in Node 8+ haben eine eingebaute Methode zum Versprechen von Nodebacks. Zum Beispiel

var getStuffAsync = Promise.promisify(getStuff); // Bluebird
var getStuffAsync = Q.denodeify(getStuff); // Q
var getStuffAsync = util.promisify(getStuff); // Native promises, node only

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

Hier gibt es keine goldene Regel, Sie versprechen es ihnen nacheinander. Einige Promise-Implementierungen ermöglichen es Ihnen jedoch, dies in großen Mengen zu tun, zum Beispiel in Bluebird, das Konvertieren einer Nodeback-API in eine Promise-API ist so einfach wie:

Promise.promisifyAll(API);

Oder mit einheimische Versprechen in Knoten:

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

Anmerkungen:

  • Natürlich, wenn Sie in einem sind .then Handler brauchen Sie nichts zu versprechen. Zurückgeben eines Versprechens von a .then Der Handler wird mit dem Wert dieses Versprechens auflösen oder ablehnen. Werfen von a .then Der Handler ist auch eine gute Übung und wird das Versprechen ablehnen – dies ist das berühmte Versprechen der Wurfsicherheit.
  • In einem tatsächlichen onload Fall sollten Sie verwenden addEventListener eher, als onX.

  • Benjamin, ich habe Ihre Einladung zur Bearbeitung angenommen und ein weiteres jQuery-Beispiel zu Fall 2 hinzugefügt. Es muss von Experten überprüft werden, bevor es angezeigt wird. Hoffe du magst es.

    – Roamer-1888

    19. April 2014 um 10:43 Uhr

  • @ Roamer-1888 wurde abgelehnt, da ich es nicht rechtzeitig gesehen und akzeptiert habe. Für das, was es wert ist, denke ich nicht, dass der Zusatz zu relevant ist, obwohl er nützlich ist.

    – Benjamin Grünbaum

    24. April 2014 um 9:44 Uhr

  • Benjamin, ob oder nicht resolve() und reject() geschrieben sind, um wiederverwendbar zu sein, gehe ich davon aus, dass meine vorgeschlagene Bearbeitung relevant ist, da sie ein jQuery-Beispiel des Formulars bietet $.Deferred(fn), was sonst fehlt. Wenn nur ein jQuery-Beispiel enthalten ist, schlage ich vor, dass es eher diese Form haben sollte var d = $.Deferred(); usw., da die Menschen ermutigt werden sollten, das oft Vernachlässigte zu verwenden $.Deferred(fn) Form, und in einer Antwort wie dieser wird jQuery mehr auf eine Stufe mit Bibliotheken gestellt, die die verwenden Aufschlussreiches Konstruktormuster.

    – Roamer-1888

    24. April 2014 um 19:36 Uhr

  • Heh, um 100% fair zu sein, ich wusste nicht, dass jQuery Sie tun lässt $.Deferred(fn)wenn Sie das in den nächsten 15 Minuten anstelle des vorhandenen Beispiels bearbeiten, bin ich sicher, dass ich versuchen kann, es rechtzeitig zu genehmigen 🙂

    – Benjamin Grünbaum

    24. April 2014 um 19:39 Uhr

  • Dies ist eine großartige Antwort. Vielleicht möchten Sie es aktualisieren, indem Sie es auch erwähnen util.promisify, die Node.js ab RC 8.0.0 zu seinem Kern hinzufügen wird. Es funktioniert nicht viel anders als Bluebird Promise.promisify, hat aber den Vorteil, dass keine zusätzlichen Abhängigkeiten erforderlich sind, falls Sie nur natives Promise möchten. Ich habe einen Blogbeitrag darüber geschrieben util.promisify für alle, die mehr zum Thema lesen möchten.

    – Bruno

    16. Mai 2017 um 5:27 Uhr


Wie konvertiere ich eine vorhandene Callback API in Promises
efkan

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

Ein einfaches und grundlegendes Beispiel für Promise (mit KUSS Weg):

Einfach JavaScript-Async-API-Code:

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

    if (divider == 0) {
        return errorCallback( new Error("Division by zero") )
    }

    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 by zero") )
        }

        fulfilled( number / divider )

     })

}

(Ich empfehle den Besuch diese schöne Quelle)

Ebenfalls Promise kann mit zusammen verwendet werden async\await in ES7 um den Programmablauf auf a warten zu lassen fullfiled Ergebnis wie folgt:

function getName () {

    return new Promise(function (fulfilled, rejected) {

        var name = "John Doe";

        // wait 3000 milliseconds before calling fulfilled() method
        setTimeout ( 
            function() {
                fulfilled( name )
            }, 
            3000
        )

    })

}


async function foo () {

    var name = await getName(); // awaits for a fulfilled result!

    console.log(name); // the console writes "John Doe" after 3000 milliseconds

}


foo() // calling the foo() method to run the code

Eine weitere Verwendung mit demselben Code durch Verwendung .then() Methode

function getName () {

    return new Promise(function (fulfilled, rejected) {

        var name = "John Doe";

        // wait 3000 milliseconds before calling fulfilled() method
        setTimeout ( 
            function() {
                fulfilled( name )
            }, 
            3000
        )

    })

}


// the console writes "John Doe" after 3000 milliseconds
getName().then(function(name){ console.log(name) })

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

Bonus: Ein hybrid Methode
(Es wird davon ausgegangen, dass die Callback-Methode 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 by zero")
            callback && callback( error )
            return rejected( error )
        }

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

     })

}

Die obige Methode kann das Ergebnis für altmodische Callback- und Promise-Verwendungen beantworten.

Hoffe das hilft.

  • Diese scheinen nicht zu zeigen, wie man in Versprechen umwandelt.

    – Dmitri Zaitsev

    18. April 2018 um 9:55 Uhr

1646891713 490 Wie konvertiere ich eine vorhandene Callback API in Promises
Shiva Kannan

Vor dem Konvertieren einer Funktion als Promise In Node.JS

var request = require('request'); //http wrapped module

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 Konvertierung

var request = require('request');

function requestWrapper(url) {
  return new Promise(function (resolve, reject) { //returning promise
    request.get(url, function (err, response) {
      if (err) {
        reject(err); //promise reject
      }else{
        resolve(response); //promise resolve
      }
    })
  })
}


requestWrapper('http://localhost:8080/promise_request/1').then(function(response){
    console.log(response) //resolve callback(success)
}).catch(function(error){
    console.log(error) //reject callback(failure)
})

Falls Sie mehrere Anfragen bearbeiten 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);//result will be array which contains each promise response
}).catch(function (err) {
  console.log(err)
});

Wie konvertiere ich eine vorhandene Callback API in Promises
Löwe

Ich glaube nicht window.onload Der Vorschlag von @Benjamin funktioniert immer, da er nicht erkennt, ob er nach dem Laden aufgerufen wird. Ich bin schon oft davon gebissen worden. 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);

1646891713 106 Wie konvertiere ich eine vorhandene Callback API in Promises
Josiah Nyarega

Eine einfache generische Funktion, die ich normalerweise verwende.

const promisify = (fn, ...args) => {
  return new Promise((resolve, reject) => {
    fn(...args, (err, data) => {
      if (err) {
        return reject(err);
      }
      resolve(data);
    });
  });
};

Wie man es benutzt

  • Die Funktion promisify akzeptiert eine Funktion mit einem Callback:
   const cb = (result) => `The result is ${result}`;

   const sum = (a, b, cb) => {
    const result = a + b;
    cb(result); // passing args to the callback function
   }


  // using the util
  promise = promisify(sum, 3, 1, cb);
  promise.then(x => console.log(x)) // 4

Sie suchen wahrscheinlich nicht nach dieser Antwort, aber dies wird Ihnen helfen, das Innenleben der verfügbaren Dienstprogramme zu verstehen

  • Ich versuche, das zu benutzen, aber wenn ich anrufe promisify(fn, arg1, arg2).then(() => { alert("Done!"); }); Der Alarm wird nie ausgelöst. Würden Sie erwarten, dass dies funktioniert?

    – Philipp Stratford

    4. Oktober 2021 um 14:22 Uhr


  • Danke, @Philip Stratford für die Frage. Die promisify wird verwendet, um eine Funktion mit einem Callback in ein Promise umzuwandeln. Ich werde meine Antwort aktualisieren, um dies zu erklären.

    – Josiah Nyarega

    15. Oktober 2021 um 17:34 Uhr

  • Ich freue mich über Vorschläge zu dieser Lösung, cc @Philip Stratford. Danke

    – Josiah Nyarega

    15. Oktober 2021 um 17:42 Uhr

Node.js 8.0.0 enthält eine neue util.promisify() API, mit der Standard-APIs im Callback-Stil von Node.js in eine Funktion eingeschlossen werden können, die ein Promise zurückgibt. Ein Beispiel für die Verwendung von util.promisify() ist unten gezeigt.

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

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

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

Sehen Verbesserte Unterstützung für Promises

  • Ich versuche, das zu benutzen, aber wenn ich anrufe promisify(fn, arg1, arg2).then(() => { alert("Done!"); }); Der Alarm wird nie ausgelöst. Würden Sie erwarten, dass dies funktioniert?

    – Philipp Stratford

    4. Oktober 2021 um 14:22 Uhr


  • Danke, @Philip Stratford für die Frage. Die promisify wird verwendet, um eine Funktion mit einem Callback in ein Promise umzuwandeln. Ich werde meine Antwort aktualisieren, um dies zu erklären.

    – Josiah Nyarega

    15. Oktober 2021 um 17:34 Uhr

  • Ich freue mich über Vorschläge zu dieser Lösung, cc @Philip Stratford. Danke

    – Josiah Nyarega

    15. Oktober 2021 um 17:42 Uhr

1646891714 367 Wie konvertiere ich eine vorhandene Callback API in Promises
Bruno

Im Release Candidate für Node.js 8.0.0 gibt es ein neues Dienstprogramm, util.promisify (Ich habe darüber geschrieben util.promisify), die die Fähigkeit verkörpert, jede beliebige Funktion zu versprechen.

Es unterscheidet sich nicht wesentlich von den in den anderen Antworten vorgeschlagenen Ansätzen, hat jedoch den Vorteil, dass es sich um eine Kernmethode handelt und keine zusätzlichen Abhängigkeiten erforderlich sind.

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

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

Dann hast du ein readFile Methode, die eine native zurückgibt Promise.

readFile('./notes.txt')
  .then(txt => console.log(txt))
  .catch(...);

  • Hey, ich (OP) habe tatsächlich vorgeschlagen util.promisify zweimal (damals im Jahr 2014, als diese Frage geschrieben wurde, und vor ein paar Monaten – worauf ich als Kernmitglied von Node gedrängt habe und die aktuelle Version ist, die wir in Node haben). Da es noch nicht öffentlich verfügbar ist, habe ich es dieser Antwort noch nicht hinzugefügt. Wir würden uns jedoch sehr über Feedback zur Nutzung freuen und wissen, was einige Fallstricke sind, um bessere Dokumente für die Veröffentlichung zu haben 🙂

    – Benjamin Grünbaum

    16. Mai 2017 um 12:53 Uhr

  • Darüber hinaus möchten Sie vielleicht das benutzerdefinierte Flag zum Versprechen besprechen util.promisify in deinem Blogbeitrag 🙂

    – Benjamin Grünbaum

    16. Mai 2017 um 12:55 Uhr

  • @BenjaminGruenbaum Meinst du die Tatsache, dass die Verwendung von util.promisify.custom Symbol ist es möglich, das Ergebnis von util.promisify zu überschreiben? Um ehrlich zu sein, war dies ein absichtlicher Fehler, da ich noch keinen nützlichen Anwendungsfall finden konnte. Vielleicht könnt ihr mir ein paar Inputs geben?

    – Bruno

    16. Mai 2017 um 16:23 Uhr

  • Sicher, betrachten Sie APIs wie fs.exists oder APIs, die nicht der Node-Konvention folgen – ein Bluebird Promise.promisify würde sie falsch verstehen, aber util.promisify macht sie richtig.

    – Benjamin Grünbaum

    16. Mai 2017 um 19:05 Uhr

986500cookie-checkWie konvertiere ich eine vorhandene Callback-API in Promises?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy