Cuando se utiliza una devolución de llamada simple como en el siguiente ejemplo:
test() {
api.on( 'someEvent', function( response ) {
return response;
});
}
¿Cómo se puede cambiar la función para usar async / await? Específicamente, asumiendo que se garantiza que 'someEvent' se llamará una vez y solo una vez, me gustaría que la prueba de función sea una función asíncrona que no regrese hasta que se ejecute la devolución de llamada, como por ejemplo:
async test() {
return await api.on( 'someEvent' );
}
Respuestas:
async/await
no es magia. Una función asincrónica es una función que puede desenvolver promesas por usted, por lo queapi.on()
deberá devolver una promesa para que 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 en sí mismas, por lo que en realidad no obtendrá el valor
test()
, sino una Promesa por un valor, que puede usar así:async function whatever() { // snip const response = await test(); // use response here // snip }
fuente
const apiOn = (event) => new Promise(resolve => api.on(event, resolve));
Es molesto que no haya una solución sencilla, y la envoltura
return new Promise(...)
es fea, pero he encontrado una solución alternativa aceptableutil.promisify
(en realidad, también hace la misma envoltura, solo que 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 uno
Promise
de losresponse
pasadoscallback
haciendo:const util = require('util'); const asyncFunction = util.promisify(voidFunction);
Ahora podemos realmente
await
elcallback
.async function test() { return await asyncFunction(args); }
Algunas reglas al usar
util.promisify
callback
debe ser el último argumento de la función que va a serpromisify
(err, res) => {...}
Lo curioso es que no necesitamos escribir nunca específicamente lo que
callback
realmente es.fuente
async / await es mágico. Puedes 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 argumentos es variable.
fuente
Puede lograr esto sin devoluciones de llamada, use promise async await 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)'/>
fuente