Docs Menu

Docs HomeDevelop ApplicationsMongoDB DriversNode.js Driver

Promises

On this page

  • Overview
  • Promises
  • Await
  • Operational Considerations

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.

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 more 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, see the Node.js API documentation.

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 more information, see the MDN documentation on await.

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 more 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());
}
}
←  Specify How CRUD Operations Run on Replica SetsAggregation →