Promessas
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 em 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 Promise é 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 Promise estará no estado Pending se a operação ainda estiver em execução, Fulfilled se a operação tiver sido concluída com êxito e Rejected se a operação tiver gerado 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()); } }