Promises
On this page
Overview
The Node.js driver uses the asynchronous Javascript API to communicate with your MongoDB cluster.
Asynchronous Javascript allows you to execute operations without waiting for the processing thread to become free. This helps prevent your application from becoming unresponsive when executing long-running operations. For more information about asynchronous Javascript, see the MDN web documentation on Asynchronous Javascript.
This section describes Promises
that you can use with the Node.js driver to
access the results of your method calls to your MongoDB cluster.
Promises
A Promise is an object returned by the asynchronous method call that allows you to access information on the eventual success or failure of the operation that they wrap. The Promise is in the Pending state if the operation is still running, Fulfilled if the operation completed successfully, and Rejected if the operation threw an exception. For more information on Promises and related terminology, see the MDN documentation on Promises.
Most driver methods that communicate with your MongoDB cluster, such as
findOneAndUpdate()
and countDocuments()
, return Promise
objects and already contain logic to handle the success or failure of the
operation.
You can define your own logic that executes once the Promise reaches the
Fulfilled or Rejected state by appending the then()
method.
The first parameter of then()
is the method that gets called when the
Promise reaches the Fulfilled state and the optional second parameter is
the method that gets called when it reaches the Rejected state. The
then()
method returns a Promise to which you can append additional
then()
methods.
When you append one or more then()
methods to a Promise, each call passes
its execution result to the next one. This pattern is called
Promise chaining. The following code snippet shows an example of Promise
chaining by appending a single then()
method.
collection .updateOne({ name: "Mount McKinley" }, { $set: { meters: 6190 } }) .then( res => console.log(`Updated ${res.result.n} documents`), err => console.error(`Something went wrong: ${err}`), );
To handle only Promise transitions to the Rejected state, use the catch()
method
rather than passing a null
first parameter to then()
. The catch()
method
accepts a single callback that is executed when the Promise transitions to the Rejected
state.
The catch()
method is often appended at the end of a Promise chain to
handle any exceptions thrown. The following code snippet demonstrates appending
a catch()
method to the end of a Promise chain.
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}`));
Note
Certain methods in the driver such as find()
return a Cursor
instead of a Promise. To determine what type each method returns, refer to
the Node.js API documentation.
Await
If you are using async
functions, you can use the await
operator on
a Promise to pause further execution until the Promise reaches either the
Fulfilled or Rejected state and returns. Since the await
operator
waits for the resolution of the Promise, you can use it in place of
Promise chaining to sequentially execute your logic. The following code
snippet uses await
to execute the same logic as the first Promise
chaining example.
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}`); } }
For additional information, see the MDN documentation on await.
Operational Considerations
One common mistake when using async
methods is to forget to use await
operator on Promises to get the value of the result rather than the Promise
object. Consider the following example in which we iterate over a cursor
using hasNext()
, which returns a Promise that resolves to a boolean that
indicates whether additional results exist, and next()
which returns a
Promise that resolves to the next entry the cursor is pointing to.
async function run() { ... // WARNING: this snippet may cause an infinite loop const cursor = myColl.find(); while (cursor.hasNext()) { console.log(cursor.next()); } }
Since the call to hasNext()
returns a Promise
, the conditional
statement returns true
regardless of the value that it resolves to.
If we alter the code to await
the call to next()
only, as demonstrated
in the following code snippet, it throws the following 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()); } }
While hasNext()
is not called until after the result of next()
returns,
the call to hasNext()
returns a Promise which evaluates to true
rather
than the value it resolves to, similar to the prior example. The code
attempts to call next()
on a Cursor that has already returned its results
and closed as a result.
If we alter the code to only await
the call to hasNext()
as shown in
the following example, the console prints Promise objects rather than the
document objects.
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
before both the hasNext()
and next()
method calls to
ensure that you are operating on the correct return values as demonstrated
in the following code:
async function run() { ... const cursor = myColl.find(); while (await cursor.hasNext()) { console.log(await cursor.next()); } }