Overview
El controlador Node.js utiliza la API asíncrona de JavaScript para comunicarse con tu clúster de MongoDB.
JavaScript asíncrono te permite ejecutar operaciones sin tener que esperar a que el hilo de procesamiento esté libre. Esto ayuda a evitar que tu aplicación se vuelva inoperante al ejecutar operaciones de larga duración. Para obtener más información sobre JavaScript asíncrono, consulta la documentación web de MDN sobre JavaScript asíncrono.
Esta sección describe Promises que puedes usar con el driver Node.js para acceder a los resultados de tus llamadas a métodos en tu clúster de MongoDB.
Promesas
Una Promesa (Promise) es un objeto retornado por la llamada de un método asíncrono que te permite acceder a información sobre el eventual éxito o fracaso de la operación que envuelve. The Promise está en estado de Pendiente si la operación aún se está ejecutando, Completada si la operación se completó con éxito, y Rechazada si la operación arrojó una excepción. Para obtener más información sobre Promises y la terminología relacionada, consulta la documentación sobre Promises.en MDN.
La mayoría de los métodos del driver que se comunican con tu clúster de MongoDB, como findOneAndUpdate() y countDocuments(), devuelve objetos Promise y ya contienen la lógica para gestionar el éxito o el fracaso de la operación.
Puedes definir tu propia lógica que se ejecuta una vez que el Promise alcanza el estado Cumplido o Rechazado añadiendo el método then(). El primer parámetro de then() es el método que se llama cuando la Promise alcanza el estado Cumplido y el segundo parámetro opcional es el método que se llama cuando alcanza el estado Rechazado. El método then() devuelve una Promise a la que puedes añadir más métodos then().
Al añadir uno o más then() métodos a una Promesa, cada llamada pasa el resultado de su ejecución a la siguiente. Este patrón se denomina encadenamiento de promesas. El siguiente fragmento de código muestra un ejemplo de encadenamiento de promesas añadiendo un solo then() método.
collection .updateOne({ name: "Mount McKinley" }, { $set: { meters: 6190 } }) .then( res => console.log(`Updated ${res.result.n} documents`), err => console.error(`Something went wrong: ${err}`), );
Para gestionar únicamente las transiciones de Promesa al estado Rechazado, utiliza el método catch() en lugar de pasar un primer parámetro null a then(). El método catch() acepta una sola función de retorno que se ejecuta cuando la Promesa transita al estado Rechazado.
El método catch() suele añadirse al final de una cadena de promesas para gestionar cualquier excepción lanzada. El siguiente fragmento de código muestra cómo añadir un método catch() al final de una cadena de promesas.
deleteOne({ name: "Mount Doom" }) .then(result => { if (result.deletedCount !== 1) { throw "Could not find Mount Doom!"; } return new Promise((resolve, reject) => { ... }); }) .then(result => console.log(`Vanquished ${result.quantity} Nazgul`)) .catch(err => console.error(`Fatal error occurred: ${err}`));
Nota
Algunos métodos del controlador, como, devuelven find() un en Cursor lugar de una promesa. Para determinar el tipo de retorno de cada método, consulte la documentación de la API de Node.js.
Esperar
Si estás utilizando funciones async, puedes utilizar el operador await sobre una Promise para pausar la ejecución hasta que la Promise alcance el estado de Cumplida o Rechazada y retorne. Dado que el operador await espera a que se resuelva la Promise, puedes usarlo en lugar de la vinculación de Promises para ejecutar tu lógica secuencialmente. El siguiente fragmento de código utiliza await para ejecutar la misma lógica que el primer ejemplo de encadenamiento de Promise.
async function run() { ... try { res = await myColl.updateOne( { name: "Mount McKinley" }, { $set: { meters: 6190 } }, ); console.log(`Updated ${res.result.n} documents`); } catch (err) { console.error(`Something went wrong: ${err}`); } }
Para obtener más información, consulta la documentación de MDN sobre await.
Consideraciones operativas
Un error común al usar async métodos es olvidar usar el await operador en Promises para obtener el valor del resultado en lugar del objeto Promise. Considera el siguiente ejemplo en el que iteramos sobre un cursor usando hasNext(), que devuelve una Promise que se resuelve en un valor booleano que indica si existen más resultados, y usando next(), que devuelve una Promise que se resuelve en la siguiente entrada a la que apunta el cursor.
async function run() { ... // WARNING: this snippet may cause an infinite loop const cursor = myColl.find(); while (cursor.hasNext()) { console.log(cursor.next()); } }
Dado que la llamada a hasNext() devuelve un Promise, la instrucción condicional devuelve true independientemente del valor al que se resuelva.
Si modificamos el código para await llamar únicamente a next(), como se muestra en el siguiente fragmento de código, se genera el siguiente error: MongoError: Cursor is closed.
async function run() { ... // WARNING: this snippet throws a MongoError const cursor = myColl.find(); while (cursor.hasNext()) { console.log(await cursor.next()); } }
Mientras que hasNext() no se llama hasta después de que se devuelve el resultado de next(), la llamada a hasNext() devuelve una Promise que se evalúa como true en lugar del valor al que se resuelve, similar al ejemplo anterior. El código intenta llamar a next() en un Cursor que ya ha devuelto sus resultados y se ha cerrado como resultado.
Si modificamos el código para solo await la llamada a hasNext() como se muestra en el siguiente ejemplo, la consola imprime objetos Promise en lugar de objetos de documento.
async function run() { ... // WARNING: this snippet prints Promises instead of the objects they resolve to const cursor = myColl.find(); while (await cursor.hasNext()) { console.log(cursor.next()); } }
Utiliza await antes de ambas llamadas a los métodos hasNext() y next() para asegurarte de que estás operando sobre los valores de retorno correctos, como se demuestra en el siguiente código:
async function run() { ... const cursor = myColl.find(); while (await cursor.hasNext()) { console.log(await cursor.next()); } }