Hay una pequeña pero importante diferencia entre una función que simplemente devuelve una promesa y una función que se declaró utilizando la
async
.
Eche un vistazo al siguiente fragmento de código:
function fn(obj) { const someProp = obj.someProp return Promise.resolve(someProp) } async function asyncFn(obj) { const someProp = obj.someProp return Promise.resolve(someProp) } asyncFn().catch(err => console.error('Catched'))
Como puede ver, ambas funciones tienen el mismo cuerpo en el que estamos tratando de acceder a la propiedad de un argumento que no está definido en ambos casos. La única diferencia entre las dos funciones es que
asyncFn
declara utilizando la
async
.
Esto significa que JavaScript garantiza que la función
asnycFn
devuelva una promesa (ya sea exitosa o fallida), incluso si se produce un error en ella, en nuestro caso el bloque
.catch()
detectará.
Sin embargo, en el caso de la función
fn
, el motor aún no sabe que la función devolverá una promesa y, por lo tanto, el código no alcanzará el bloque
.catch()
, el error no se detectará y caerá en la consola.
Más ejemplo de vida
Sé lo que estás pensando ahora:
"¿Cuándo diablos cometeré tal error?"
Adivinado?
Bueno, creemos una aplicación simple que haga exactamente eso.
Supongamos que tenemos una aplicación creada con Express y MongoDB que usa el controlador MongoDB Node.JS. Si no confía en mí, he colocado todo el código fuente en
este repositorio de Github , para que pueda clonarlo y ejecutarlo localmente, pero también duplicaré todo el código aquí.
Aquí está nuestro archivo
app.js
:
¡Mira de cerca el bloque
.catch()
! Aquí es donde ocurrirá la magia (no).
El archivo
db.js
utiliza para conectarse a la base de datos mongo:
'use strict' const MongoClient = require('mongodb').MongoClient const url = 'mongodb://localhost:27017' const dbName = 'async-promise-test' const client = new MongoClient(url) let db module.exports = { connect() { return new Promise((resolve, reject) => { client.connect(err => { if (err) return reject(err) console.log('Connected successfully to server') db = client.db(dbName) resolve(db) }) }) }, getDb() { return db } }
Y finalmente, tenemos un archivo
user-model.js
en el que actualmente solo se define una función
getUserById
:
Si
app.js
a mirar el archivo
app.js
, verá que cuando vamos a
localhost:3000/users/<id>
llamamos a la función
getUserById
definida en el
user-model.js
, pasando el parámetro
id
como solicitud.
Digamos que usted va a la siguiente dirección:
localhost:3000/users/1
. ¿Qué crees que pasará después?
Bueno, si respondiste: "Veré un gran error de MongoClient", tenías razón. Para ser más precisos, verá el siguiente error:
Error: Argument passed in must be a single String of 12 bytes or a string of 24 hex characters
.
¿Y crees que se
.catch()
bloque
.catch()
en el siguiente fragmento de código?
No No será llamado.
¿Y qué sucede si cambia una declaración de función a esto?
module.exports = {
Sí, comienzas a entender qué es qué. Se
.catch()
nuestro bloque
.catch()
, y podremos procesar el error detectado y mostrárselo al usuario.
En lugar de una conclusión
Espero que esta información haya sido útil para algunos de ustedes. Tenga en cuenta que este artículo no intenta obligarlo a usar siempre funciones asincrónicas, aunque son bastante geniales. Tienen sus propios casos de uso, pero siguen siendo azúcar sintáctica por encima de las promesas.
Solo quería que supieras que a veces las promesas pueden hacer una gran diferencia, y cuando (sí, no "si") encuentras el error discutido en este artículo, sabrás la posible razón de su ocurrencia.
Nota PS perev.: al artículo original, se dejó un comentario útil del usuario Craig P Hicks, que (después de los comentarios en los comentarios) decidí citar aquí:Me gustaría llamar la atención sobre un detalle, (en mi entorno de desarrollo) los errores que ocurren en el cuerpo de Promise.resolve({<body>})
no se "detectan":
Promise.resolve((()=>{throw "oops"; })()) .catch(e=>console("Catched ",e));
pero los errores que ocurren en el cuerpo de la new Promise()
( aprox. traducción: en la "Promesa apropiada" original ) son "atrapados":
(new Promise((resolve,reject)=>{ resolve((()=>{throw "oops"})()) })) .catch(e=>console.log("Catched ",e));
¿Qué tal esta declaración:
async function fn() { <body> }
semánticamente, esta opción es equivalente a esto:
function fn() { return new Promise((resolve,reject)=>{ resolve({ <body> }) }) }
En consecuencia, el siguiente fragmento de código detectará errores si el <cuerpo> tiene una new Promise()
( aprox. Traducción: en la "Promesa apropiada" original ):
function fn() { return Promise.resolve({<body}); }
Por lo tanto, para que el ejemplo del principio del artículo "atrape" los errores en ambos casos, es necesario devolver no Promise.resolve()
, sino new Promise()
en las funciones: function fn(obj) { return new Promise((resolve, reject) => { const someProp = obj.someProp; resolve(someProp); }); } async function asyncFn(obj) { return new Promise((resolve, reject) => { const someProp = obj.someProp; resolve(someProp); }); } asyncFn().catch(err => console.error("Catched"));