Visão geral
O driver do Node.js usa a API Javascript assíncrona para se comunicar com seu cluster MongoDB.
JavaScript assíncrono permite que você execute operações sem esperar que a thread de processamento fique livre. Isso ajuda a evitar que seu aplicação pare de responder quando executa operações de longa duração. Para obter mais informações sobre JavaScript assíncrono, consulte a documentação da web MDN sobre JavaScript assíncrono.
Esta seção descreve Promises que você pode usar com o driver Node.js para acessar os resultados de suas chamadas de método para o cluster MongoDB.
Promessas
Uma Promessa é um objeto retornado pela chamada de método assíncrono que permite acessar informações sobre o eventual sucesso ou fracasso da operação que ela envolve. A Promessa estará no estado Pendente se a operação ainda estiver em execução, Cumprida se a operação for concluída com êxito e Rejeitada se a operação gerar uma exceção. Para obter mais informações sobre Promises e terminologia relacionada, consulte a documentação MDN sobre Promises.
A maioria dos métodos de driver que se comunicam com o cluster MongoDB, como findOneAndUpdate() e countDocuments(), retornam objetos Promessa e já contêm lógica para lidar com o sucesso ou a falha da operação.
Você pode definir sua própria lógica que será executada quando a Promessa atingir o estado Cumprido ou Rejeitado anexando o método then(). O primeiro parâmetro de then() é o método que é chamado quando a Promessa atinge o estado Cumprido e o segundo parâmetro opcional é o método que é chamado quando atinge o estado Rejeitado. O método then() retorna uma promessa à qual você pode acrescentar mais métodos then().
Quando você anexa um ou mais métodos do then() a uma Promessa, cada chamada passa seu resultado de execução para a próxima. Esse padrão é chamado de Encadeamento de Promessas. O seguinte trecho de código mostra um exemplo de cadeia de Promessa anexando um único método then().
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 lidar somente com transições de promessa para o estado Rejeitado, use o método catch() em vez de passar o primeiro parâmetro null para then(). O método catch() aceita um único retorno de chamada que é executado quando a Promessa transita para o estado Rejeitada.
O método catch() geralmente é anexado no final de uma cadeia de Promessa para lidar com quaisquer exceções lançadas. O seguinte trecho de código demonstra anexar um método catch() ao final de uma cadeia de Promessa.
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}`));
Observação
Alguns métodos no driver, como find(), retornam um Cursor em vez de uma promessa. Para determinar o tipo que cada método retorna, consulte a documentação da API do Node.js
Aguardam
Se você estiver usando async funções, poderá usar o operador await em uma Promessa para pausar a execução adicional até que a Promessa atinja o estado Cumprido ou Rejeitado e retorne. Como o operador await aguarda a resolução da Promessa, você pode usá-lo no lugar do encadeamento da Promessa para executar sequencialmente sua lógica. O seguinte trecho de código usa await para executar a mesma lógica que o primeiro exemplo de cadeia de Promessa.
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 obter mais informações, consulte a documentação da MDN sobre await.
Considerações operacionais
Um erro comum ao usar os métodos async é esquecer de usar o operador await nas Promessas para obter o valor do resultado em vez do objeto Promessa. Considere o exemplo a seguir, no qual iteramos em um cursor usando hasNext(), o qual retorna uma Promessa que resolve para um boolean que indica se existem mais resultados, e next(), o qual retorna uma Promessa que resolve para a próxima entrada para a qual o cursor está apontando.
async function run() { ... // WARNING: this snippet may cause an infinite loop const cursor = myColl.find(); while (cursor.hasNext()) { console.log(cursor.next()); } }
Como a chamada para hasNext() retorna uma Promise, a declaração condicional retorna true, independentemente do valor para o qual é resolvida.
Se alterarmos o código para await a chamada apenas para next() , conforme demonstrado no trecho de código a seguir, ela apresentará o seguinte erro: 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()); } }
Enquanto hasNext() não é chamado até que o resultado de next() retorne, a chamada para hasNext() retorna uma Promessa que avalia para true em vez do valor para o qual ele resolve, semelhante ao exemplo anterior. O código tenta ligar para next() em um cursor que já devolveu seus resultados e fechou como resultado.
Se alterarmos o código para apenas await a chamada para hasNext(), conforme mostrado no exemplo a seguir, o console imprimirá os objetos Promessa em vez dos 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()); } }
Use await antes que os métodos hasNext() e next() chamem para garantir que você está operando com os valores de retorno corretos, conforme demonstrado no seguinte código:
async function run() { ... const cursor = myColl.find(); while (await cursor.hasNext()) { console.log(await cursor.next()); } }