Cuando se utiliza una devolución de llamada simple como en el ejemplo a continuación:

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

¿Cómo se puede cambiar la función para usar async / await? Específicamente, suponiendo que se garantice que 'someEvent' se llame una vez y solo una vez, me gustaría que la prueba de función sea una función asíncrona que no regresa hasta que se ejecuta la devolución de llamada, como:

async test() {
  return await api.on( 'someEvent' );
}
102
sean2078 8 may. 2016 a las 22:56

4 respuestas

La mejor respuesta

async/await no es mágico. Una función asíncrona es una función que puede desenvolver Promesas para usted, por lo que necesitará api.on() para devolver una Promesa para que eso funcione. Algo como esto:

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

Entonces

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

Pero eso también es una mentira, porque las funciones asíncronas también devuelven Promesas por sí mismas, por lo que no obtendrá el valor de test(), sino una Promesa para un valor, que puede usar así:

async function whatever() {
  // snip
  const response = await test();
  // use response here
  // snip
}
114
Madara's Ghost 1 sep. 2017 a las 11:49

Asíncrono / esperar es mágico. Puede crear una función asPromise para manejar este tipo de situaciones:

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

Y luego úsalo cuando quieras:

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

El número de args es variable.

0
YakovL 4 ago. 2019 a las 11:32

Es molesto que no haya una solución sencilla, y la envoltura return new Promise(...) es fugosa, pero he encontrado una solución correcta usando util.promisify (en realidad también hace la misma envoltura, solo se ve mejor) .

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

La función anterior no devuelve nada, todavía. Podemos hacer que devuelva un Promise del response pasado en callback haciendo:

const util = require('util');

const asyncFunction = util.promisify(voidFunction);

Ahora podemos await la callback.

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

Algunas reglas al usar util.promisify

  • El callback debe ser el último argumento de la función que será promisify
  • La supuesta devolución de llamada debe estar en la forma (err, res) => {...}

Lo curioso es que no necesitamos escribir específicamente cuál es realmente el callback.

5
ErikD 13 sep. 2018 a las 08:42

Puede lograr esto sin devoluciones de llamada, use la promesa de espera asíncrona en lugar de devoluciones de llamada aquí cómo lo haría. Y también aquí he ilustrado dos métodos para manejar errores

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)'/>
1
NuOne 4 ene. 2019 a las 05:43