Wie kann man auf einen Rückruf warten?

92

Bei Verwendung eines einfachen Rückrufs wie im folgenden Beispiel:

test() {
  api.on( 'someEvent', function( response ) {
    return response;
  });
}

Wie kann die Funktion geändert werden, um async / await zu verwenden? Unter der Annahme, dass 'someEvent' garantiert einmal und nur einmal aufgerufen wird, möchte ich, dass der Funktionstest eine asynchrone Funktion ist, die erst zurückkehrt, wenn der Rückruf ausgeführt wird, wie z.

async test() {
  return await api.on( 'someEvent' );
}
sean2078
quelle
1
Nur als Referenz wurde die ES7 / ES2016-Spezifikation fertiggestellt und enthält kein Async / Warten. Im Moment ist es nur ein Vorschlag der Stufe 3 .
Dan Prince
Nun, das ist überraschend - Ich hoffe sehr, dass es aufgenommen wird! Vielen Dank für die Info @DanPrince
Sean2078

Antworten:

135

async/awaitist keine Magie. Eine asynchrone Funktion ist eine Funktion, die Versprechen für Sie auspacken kann. Sie müssen also api.on()ein Versprechen zurückgeben, damit dies funktioniert. Etwas wie das:

function apiOn(event) {
  return new Promise(resolve => {
    api.on(event, response => resolve(response));
  });
}

Dann

async function test() {
  return await apiOn( 'someEvent' ); // await is actually optional here
                                      // you'd return a Promise either way.
}

Aber das ist auch eine Lüge, denn asynchrone Funktionen geben auch Versprechen selbst zurück, sodass Sie nicht den Wert aus test()einem Versprechen herausholen, sondern ein Versprechen für einen Wert, den Sie wie folgt verwenden können:

async function whatever() {
  // snip
  const response = await test();
  // use response here
  // snip
}
Madaras Geist
quelle
3
Eine kürzere Version für die Funktion, die ein Versprechen zurückgibt: const apiOn = (event) => new Promise(resolve => api.on(event, resolve));
Felipe Plets
7

Es ist ärgerlich, dass es keine einfache Lösung gibt und das Wickeln return new Promise(...)schwierig ist, aber ich habe eine gute Umgehungsmöglichkeit gefunden util.promisify(tatsächlich macht es auch irgendwie das gleiche Wickeln, sieht einfach besser aus).

function voidFunction(someArgs, callback) {
  api.onActionwhichTakesTime(someMoreArgs, (response_we_need) => {
    callback(null, response_we_need);
  });
}

Die obige Funktion gibt noch nichts zurück. Wir können dafür sorgen, dass es eines Promiseder übergebenen responsezurückgibt, callbackindem wir Folgendes tun:

const util = require('util');

const asyncFunction = util.promisify(voidFunction);

Jetzt können wir tatsächlich awaitdie callback.

async function test() {
  return await asyncFunction(args);
}

Einige Regeln bei der Verwendung util.promisify

  • Das callbackmuss das letzte Argument der Funktion sein, die es geben wirdpromisify
  • Der angebliche Rückruf muss in der Form vorliegen (err, res) => {...}

Lustige Sache ist, dass wir nie speziell schreiben müssen, was das callbackeigentlich ist.

ErikD
quelle
2

Async / Warten ist Magie. Sie können eine Funktion erstellen asPromise, um diese Art von Situationen zu behandeln:

function asPromise(context, callbackFunction, ...args) {
    return new Promise((resolve, reject) => {
        args.push((err, data) => {
            if (err) {
                reject(err);
            } else {
                resolve(data);
            }
        });
        if (context) {
            callbackFunction.call(context, ...args);
        } else {
            callbackFunction(...args);
        }
    });
}

und verwenden Sie es dann, wenn Sie möchten:

async test() {
    return await this.asPromise(this, api.on, 'someEvent');
}

Die Anzahl der Argumente ist variabel.

negstek
quelle
1

Sie können dies ohne Rückrufe erreichen. Verwenden Sie hier das Versprechen async await anstelle von Rückrufen, wie ich dies tun würde. Und auch hier habe ich zwei Methoden zum Umgang mit Fehlern dargestellt

clickMe = async (value) => {
  
  // begin to wait till the message gets here;
  let {message, error} = await getMessage(value);
  
  // if error is not null
  if(error)
    return console.log('error occured ' + error);
   
  return console.log('message ' + message);

}

getMessage = (value) => {

  //returning a promise 
  return new Promise((resolve, reject) => {
  
    setTimeout(() => {
      // if passed value is 1 then it is a success
      if(value == 1){
        resolve({message: "**success**", error: null});
      }else if (value == 2){
        resolve({message: null, error: "**error**"});
      }
    }, 1000);
  
  });

}

clickWithTryCatch = async (value) => {

  try{
    //since promise reject in getMessage2 
    let message = await getMessage2(value);
    console.log('message is ' + message);
  }catch(e){
    //catching rejects from the promise
    console.log('error captured ' + e);
  }

}

getMessage2 = (value) => {

  return new Promise((resolve, reject) => {
  
    setTimeout(() => {
      if(value == 1)
        resolve('**success**');
      else if(value == 2)
        reject('**error**'); 
    }, 1000);
  
  });

}
<input type='button' value='click to trigger for a value' onclick='clickMe(1)' />
<br/>
<input type='button' value='click to trigger an error' onclick='clickMe(2)' />
<br/>
<input type='button' value='handling errors with try catch' onclick='clickWithTryCatch(1)'/>
<br/>
<input type='button' value='handling errors with try catch' onclick='clickWithTryCatch(2)'/>

NuOne
quelle