Michael_K: Callback in Promise umschreiben

Hallo,

ich habe mich in den letzten Tagen in die Funktionsweisen von Promise eingelesen. Finde ich sehr spannend und in meiner Arbeit nützlich. Aber irgendwie habe ich noch nicht ganz den Trick raus, wie ich meine bestehenden Funktionen mit callback in ein Promise umschreibe. Bzw. möchte ich erst einmal einen Wrapper um meine bestehenden Funktionen. Ich hatte gehoffts, dass es relative einfach ist, aber irgendetwas klappt da nicht.

Meine bestehende asynchrone workerFunktion mit callback sehen vereinfacht so aus und nachfolgend mein Versuch, diese n einen Promise umzuschrieben. aber die promiseWrapper function funktioniert nicht.

var readyCB = function(result){
// arbeite mit dem callback
};

var errorCB = function(error){
//
};

function workerFunction(option, values, readyCB, errorCB){

/* hier wird asynchron mi einem webworker berechnet, beispiel vereinfacht */

    function resultReceiver(msg){
        if (readyCB) readCB(msg.data)
    }


    function errorReceiver(error){
        if (errorCB) errorCB(error)
    }

    var worker = new Worker('worker.js');
    worker.onmessage = resultReceiver;
    worker.onerror = errorReceiver;
    worker.postMessage(values);

}

// mein Versuche eines Wrappers

function promiseWrapper(){
    return new Promise(function(resolve, reject) {
        workerFunction(option, values, resolve, reject);
    });
};

Kann mir jemand sagen, wo mein Denkfehler bei dem Wrapper ist?

Gruss

  1. Hallo Michael_K,

    sieht gar nicht so falsch aus (nachtrag: Wenn das readCB gefixt ist), nur solltest Du noch option und values als Parameter an den Wrapper übergeben.

    Dass beim Aufruf des Wrappers die then und catch Methoden auf dem zurückgegebenen Promise aufgerufen werden müssen, weißt Du sicher. Aber da gibt's einen Fallstrick.

    Die typischen Beispiele sehen so aus:

    promiseWrapper(foo, bar)
    .then(readyCB)
    .catch(errorCB);
    

    Dieses Pattern ruft die errorCB Funktion auch dann auf, wenn readyCB einen Fehler produziert (sprich: eine Exception wirft oder ein rejectetes Promise zurückgibt). Das entspricht aber nicht deiner alten Verwendung. Wenn Du es exakt 1:1 haben willst, musst Du das Promise so benutzen:

    promiseWrapper(foo, bar)
    .then(readyCB, errorCB);
    

    Auf diese Weise wird errorCB nur bei einem onerror des Workers aufgerufen.

    Du gewinnst auf diese Weise aber kaum etwas. Du nimmst lediglich zwei Parameter aus dem Aufruf der workerFunction heraus und verschiebst sie in den Aufruf von then. Dafür bekommst Du die Komplexität eines Wrappers und der Promise-Klasse hinzu.

    Wenn überhaupt, solltest du die workerFunction konsequent auf Promises umschreiben:

    function promiseWork(option, values) {
       return new Promise( function(resolve, reject) {
          let worker = new Worker('worker.js');
          worker.onmessage = resolve;
          worker.onerror = reject;
          worker.postMessage(values);
       })
    }
    

    Fertig. Keine Abfragen mehr auf Callbacks, keine zwei Schichten. Wenn deine eigenen resultReceiver und errorReceiver Funktionen in der workerFunction noch weitere Dinge tun, dann kannst Du das mit internen then/catch Aufrufen erledigen:

    function promiseWork(option, values) {
       return new Promise( function(resolve, reject) {
          let worker = new Worker('worker.js');
          worker.onmessage = resolve;
          worker.onerror = reject;
          worker.postMessage(values);
       })
       .then(function(data) {
                // tu was mit den Daten
                // Daten an nächsten then weitergeben
                return data;
             },
             function(err) {
                // tu was mit dem Fehler
                // Fehler an nächsten catch weitergeben
                return Promise.reject(err);
             });
    }
    

    Natürlich bist Du damit etwas weniger flexibel als mit deiner alten workerFunction. Insbesondere gibt es keine Logik mehr, die dazu führen könnte, dass weder ready noch error Callback aufgerufen werden. Keine Ahnung ob Du sowas brauchst, das geht aus deiner Vereinfachung nicht hervor.

    Und man sollte dieses Konstrukt ggf. in weitere Funktionen zerlegen, ich vermute, dass sich da einiges an Redundanz ergibt.

    Rolf

    --
    sumpsi - posui - clusi
    1. Wow, vielen Dank Rolf.

      Die Erklärungen haben mir sehr geholfen, auch die Idee mit dem errorCB und readyCB gemeinsam als resolve auszugeben finde ich elegant. Ist gut zu wissen, dass man es auch so lösen könnte. Ich habe meinen Fehler gefunden, der zu trivial war. Anstatt .then hatte ich .than geschrieben. Arrrgh.

      Eine Frage hätte ich noch für den Wrapper. Wenn die workerFunktion oft "gleichzeitig" mit unterschiedlichen Paramtern aufgerufen wird, müsste man diese auch als neue Instanz mit einem new Operator hinterlegen, oder erfolgt das durch die Einbettung in ein Promise automatisch?

      Also:

      // so
      
      function promiseWrapper(option, values){
          return new Promise(function(resolve, reject) {
              workerFunction(option, values, resolve, reject);
          });
      };
      
      // oder so
      
      function promiseWrapper(option, values){
          return new Promise(function(resolve, reject) {
           // neue Instanz mit einem new Operator
           new workerFunction(option, values, resolve, reject);
          });
      };
      

      Gruss

      1. Hallo Michael_K,

        zwischen workerFunction() und new workerFunction() besteht ein fundamentaler Unterschied; das eine ist durch das andere nicht austauschbar. Das ist aber nicht eben mal so in einem Posting zu erklären, weil dafür eine Menge von JavaScript-Konzepten verstanden werden muss.

        Jedenfalls brauchst Du für die bisher diskutierten Dinge kein new. Mit new würdest Du ein neues Objekt erzeugen, und workerFunction wäre eine Konstruktorfunktion. Das wäre nur sinnvoll, wenn das Objekt länger lebt als nur für den Konstruktoraufruf.

        Wenn es nur darum geht, einen Worker zu erzeugen, seine Events auf resolve und reject abzubilden und dem Worker genau eine Nachricht zu schicken, dann ist ein Objekt nicht nötig.

        Diese Argumentation mag falsch sein, aber um das zu beurteilen müsste man dein Projekt genauer kennen.

        Rolf

        --
        sumpsi - posui - clusi
  2. Tach!

    aber die promiseWrapper function funktioniert nicht.

    Präzisiere bitte, was genau nicht funktioniert. An welcher Stelle bekommst du nicht das Ergebnis, das gewünscht ist? Gibt es irgendwelche Fehlermeldungen? Wenn readCB kein Copy-Paste-Fehler ist, sollte es zumindest da eine Beschwerde geben.

    dedlfix.

    1. Dank für die Antwort. Ich hätte wirklich die Fehlermeldung noch einmal genau studieren sollen. Ich hatte .than und nicht .then beim promise Aufruf verwendet.