javascript - uncaught - ¿Cómo rechazar en sintaxis asíncrona/espera?
funcion asincrona javascript (7)
Esta no es una respuesta sobre la de @TJ Crowder. Solo un comentario que responde al comentario "Y en realidad, si la excepción se va a convertir en un rechazo, no estoy seguro de si realmente me molesta si se trata de un error. Mis razones para arrojar solo el error probablemente no se apliquen. "
si su código usa
async
/
await
, entonces es una buena práctica rechazar con un
Error
lugar de
400
:
try {
await foo(''a'');
}
catch (e) {
// you would still want `e` to be an `Error` instead of `400`
}
¿Cómo puedo rechazar una promesa que devuelve una función asíncrona / espera?
por ejemplo, originalmente
foo(id: string): Promise<A> {
return new Promise((resolve, reject) => {
someAsyncPromise().then((value)=>resolve(200)).catch((err)=>reject(400))
});
}
Traducir a asíncrono / esperar
async foo(id: string): Promise<A> {
try{
await someAsyncPromise();
return 200;
} catch(error) {//here goes if someAsyncPromise() rejected}
return 400; //this will result in a resolved promise.
});
}
Entonces, ¿cómo podría rechazar adecuadamente esta promesa en este caso?
Probablemente también debería mencionarse que simplemente puede encadenar una función
catch()
después de la llamada de su operación asincrónica porque aún debajo del capó aún se devuelve una promesa.
await foo().catch(error => console.log(error));
De esta manera, puede evitar la sintaxis
try/catch
si no le gusta.
Puede crear una función de envoltura que tome una promesa y devuelva una matriz con datos si no hay error y el error si hubo un error.
function safePromise(promise) {
return promise.then(data => [ data ]).catch(error => [ null, error ]);
}
Úselo así en ES7 y en una función asíncrona :
async function checkItem() {
const [ item, error ] = await safePromise(getItem(id));
if (error) { return null; } // handle error and return
return item; // no error so safe to use item
}
Sé que esta es una vieja pregunta, pero acabo de tropezar con el hilo y parece haber una confusión entre los errores y el rechazo que entra en conflicto (al menos en muchos casos) con los consejos repetidos a menudo de no usar el manejo de excepciones para lidiar con casos anticipados. Para ilustrar: si un método asincrónico está intentando autenticar a un usuario y la autenticación falla, es un rechazo (uno de los dos casos previstos) y no un error (por ejemplo, si la API de autenticación no estaba disponible).
Para asegurarme de que no solo estaba dividiendo pelos, realicé una prueba de rendimiento de tres enfoques diferentes para eso, usando este código:
const iterations = 100000;
function getSwitch() {
return Math.round(Math.random()) === 1;
}
function doSomething(value) {
return ''something done to '' + value.toString();
}
let processWithThrow = function () {
if (getSwitch()) {
throw new Error(''foo'');
}
};
let processWithReturn = function () {
if (getSwitch()) {
return new Error(''bar'');
} else {
return {}
}
};
let processWithCustomObject = function () {
if (getSwitch()) {
return {type: ''rejection'', message: ''quux''};
} else {
return {type: ''usable response'', value: ''fnord''};
}
};
function testTryCatch(limit) {
for (let i = 0; i < limit; i++) {
try {
processWithThrow();
} catch (e) {
const dummyValue = doSomething(e);
}
}
}
function testReturnError(limit) {
for (let i = 0; i < limit; i++) {
const returnValue = processWithReturn();
if (returnValue instanceof Error) {
const dummyValue = doSomething(returnValue);
}
}
}
function testCustomObject(limit) {
for (let i = 0; i < limit; i++) {
const returnValue = processWithCustomObject();
if (returnValue.type === ''rejection'') {
const dummyValue = doSomething(returnValue);
}
}
}
let start, end;
start = new Date();
testTryCatch(iterations);
end = new Date();
const interval_1 = end - start;
start = new Date();
testReturnError(iterations);
end = new Date();
const interval_2 = end - start;
start = new Date();
testCustomObject(iterations);
end = new Date();
const interval_3 = end - start;
console.log(`with try/catch: ${interval_1}ms; with returned Error: ${interval_2}ms; with custom object: ${interval_3}ms`);
Algunas de las cosas que están allí están incluidas debido a mi incertidumbre con respecto al intérprete de Javascript (solo me gusta ir por una madriguera a la vez);
por ejemplo,
doSomething
función
dummyValue
y
dummyValue
su retorno a
dummyValue
para garantizar que los bloques condicionales no se optimizaran.
Mis resultados fueron:
with try/catch: 507ms; with returned Error: 260ms; with custom object: 5ms
Sé que hay muchos casos en los que no vale la pena buscar pequeñas optimizaciones, pero en sistemas a gran escala, estas cosas pueden hacer una gran diferencia acumulativa, y esa es una comparación bastante dura.
ASÍ… aunque creo que el enfoque de la respuesta aceptada es sólido en los casos en que espera tener que manejar errores impredecibles dentro de una función asincrónica, en los casos en que un rechazo simplemente significa "tendrá que seguir el Plan B (o C o D ...) "Creo que mi preferencia sería rechazar el uso de un objeto de respuesta personalizado.
Su mejor opción es
throw
un
Error
envuelva el valor, lo que resulta en una promesa rechazada con un
Error
envuelve el valor:
} catch (error) {
throw new Error(400);
}
También puede simplemente
throw
el valor, pero luego no hay información de seguimiento de la pila:
} catch (error) {
throw 400;
}
Alternativamente, devuelva una promesa rechazada con un
Error
ajustando el valor:
} catch (error) {
return Promise.reject(new Error(400));
}
(O simplemente
return Promise.reject(400);
pero de nuevo, entonces no hay información de contexto).
(En su caso, como está usando
TypeScript
y el valor de recuperación de
foo
es
Promise<A>
, usaría
return Promise.reject<A>(400 /*or error*/);
)
En una situación
async
/ en
await
, eso último es probablemente una coincidencia semántica, pero funciona.
Si arroja un
Error
, eso funciona bien con cualquier cosa que consuma el resultado de su
foo
con la sintaxis de
await
:
try {
await foo();
} catch (error) {
// Here, `error` would be an `Error` (with stack trace, etc.).
// Whereas if you used `throw 400`, it would just be `400`.
}
Tengo una sugerencia para manejar adecuadamente los rechazos en un enfoque novedoso, sin tener múltiples bloques try-catch.
import to from ''./to'';
async foo(id: string): Promise<A> {
let err, result;
[err, result] = await to(someAsyncPromise()); // notice the to() here
if (err) {
return 400;
}
return 200;
}
Donde se debe importar la función to.ts desde:
export default function to(promise: Promise<any>): Promise<any> {
return promise.then(data => {
return [null, data];
}).catch(err => [err]);
}
Los créditos van a Dima Grossman en el siguiente link .
Una mejor manera de escribir la función asíncrona sería devolviendo una Promesa pendiente desde el principio y luego manejando tanto los rechazos como las resoluciones dentro de la devolución de llamada de la promesa, en lugar de escupir una promesa rechazada por error. Ejemplo:
async foo(id: string): Promise<A> {
return new Promise(function(resolve, reject) {
// execute some code here
if (success) { // let''s say this is a boolean value from line above
return resolve(success);
} else {
return reject(error); // this can be anything, preferably an Error object to catch the stacktrace from this function
}
});
}
Luego, solo encadena métodos en la promesa devuelta:
async function bar () {
try {
var result = await foo("someID")
// use the result here
} catch (error) {
// handle error here
}
}
bar()
Fuente: este tutorial:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise