javascript - try - ¿Cómo "esperar" a que vuelva una devolución de llamada?
javascript wait for async response (4)
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, 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'' );
}
Es molesto que no haya una solución sencilla, y el ajuste de
return new Promise(...)
es fugoso, pero he encontrado una
util.promisify
usando
util.promisify
(en realidad también hace el mismo ajuste, 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 una
Promise
de la
response
pasada en la
callback
de
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
-
La
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 la
callback
.
Puede lograr esto sin devoluciones de llamada, utilice la promesa de espera asíncrona en lugar de devoluciones de llamada aquí, cómo haría esto. 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)''/>
asíncrono / esperar es mágico.
Puede crear una función como
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.
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 en sí mismas, por lo que no obtendrá el valor de
test()
, sino una Promesa para un valor, que puede usar de la siguiente manera:
async function whatever() {
// snip
const response = await test();
// use response here
// snip
}