Skip to content

Commit a743073

Browse files
author
Chris Cho
authored
DOCSP-9706: Promises and Callbacks (#88)
* DOCSP-9706: promises and callbacks section
1 parent 85d1caa commit a743073

File tree

3 files changed

+248
-0
lines changed

3 files changed

+248
-0
lines changed

source/fundamentals.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Fundamentals
99
/fundamentals/connection
1010
/fundamentals/authentication
1111
/fundamentals/crud
12+
/fundamentals/promises
1213
/fundamentals/indexes
1314
/fundamentals/collations
1415
/fundamentals/logging

source/fundamentals/promises.txt

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
======================
2+
Promises and Callbacks
3+
======================
4+
5+
.. default-domain:: mongodb
6+
7+
.. contents:: On this page
8+
:local:
9+
:backlinks: none
10+
:depth: 1
11+
:class: singlecols
12+
13+
Overview
14+
--------
15+
16+
The Node.js driver uses the asynchronous Javascript API to communicate with
17+
your MongoDB cluster.
18+
19+
Asynchronous Javascript allows you to execute operations without waiting for
20+
the processing thread to become free. This helps prevent your application
21+
from becoming unresponsive when
22+
executing long-running operations. For more information about asynchronous
23+
Javascript, see the MDN web documentation on
24+
`Asynchronous Javascript <https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous>`_.
25+
26+
This section describes two features of asynchronous Javascript --
27+
``Promises`` and ``Callbacks`` -- that you can use with the Node.js driver to
28+
access the results of your method calls to your MongoDB cluster.
29+
30+
Promises
31+
--------
32+
33+
A Promise is an object returned by the asynchronous method call that allows
34+
you to access information on the eventual success or failure of the operation
35+
that they wrap. The Promise is in the **Pending** state if the operation is
36+
still running, **Fulfilled** if the operation completed successfully, and
37+
**Rejected** if the operation threw an exception. For more information on
38+
Promises and related terminology, see the MDN documentation on
39+
:mdn:`Promises <Web/JavaScript/Reference/Global_Objects/Promise>`.
40+
41+
Most driver methods that communicate with your MongoDB cluster such as
42+
``findOneAndUpdate()``, ``countDocuments()``, and ``update()`` return Promise
43+
objects and already contain logic to handle the success or failure of the
44+
operation.
45+
46+
You can define your own logic that executes once the Promise reaches the
47+
**Fulfilled** or **Rejected** state by appending the ``then()`` method.
48+
The first parameter of ``then()`` is the method that gets called when the
49+
Promise reaches the **Fulfilled** state and the optional second parameter is
50+
the method that gets called when it reaches the **Rejected** state. The
51+
``then()`` method returns a Promise to which you can append additional
52+
``then()`` methods.
53+
54+
When you append one or more ``then()`` methods to a Promise, each call passes
55+
its execution result to the next one. This pattern is called
56+
**Promise chaining**. The following code snippet shows an example of Promise
57+
chaining by appending a single ``then()`` method.
58+
59+
.. code-block:: js
60+
61+
collection
62+
.updateOne({ name: "Mount McKinley" }, { $set: { meters: 6190 } })
63+
.then(
64+
res => console.log(`Updated ${res.result.n} documents`),
65+
err => console.error(`Something went wrong: ${err}`),
66+
);
67+
68+
If you only need to handle Promise transitions to the **Rejected** state,
69+
rather than passing a ``null`` first parameter to ``then()``, you can instead
70+
use the ``catch()`` method which accepts a single callback, executed when the
71+
Promise transitions to the **Rejected** state.
72+
73+
The ``catch()`` method is often appended at the end of a Promise chain to
74+
handle any exceptions thrown. The following code snippet demonstrates appending
75+
a ``catch()`` method to the end of a Promise chain.
76+
77+
.. code-block:: js
78+
79+
deleteOne({ name: "Mount Doom" })
80+
.then(result => {
81+
if (result.deletedCount !== 1) {
82+
throw "Could not find Mount Doom!";
83+
}
84+
return new Promise((resolve, reject) => {
85+
...
86+
});
87+
})
88+
.then(result => console.log(`Vanquished ${result.quantity} Nazgul`))
89+
.catch(err => console.error(`Fatal error occurred: ${err}`));
90+
91+
.. note::
92+
93+
Certain methods in the driver such as ``find()`` return a ``Cursor``
94+
instead of a Promise. To determine what type each method returns, refer to
95+
the :node-api:`Node.js API documentation <>`.
96+
97+
Await
98+
~~~~~
99+
100+
If you are using ``async`` functions, you can use the ``await`` operator on
101+
a Promise to pause further execution until the Promise reaches either the
102+
**Fulfilled** or **Rejected** state and returns. Since the ``await`` operator
103+
waits for the resolution of the Promise, you can use it in place of
104+
Promise chaining to sequentially execute your logic. The following code
105+
snippet uses ``await`` to execute the same logic as the first Promise
106+
chaining example.
107+
108+
.. code-block:: js
109+
110+
async function run() {
111+
...
112+
try {
113+
res = await collection.updateOne(
114+
{ name: "Mount McKinley" },
115+
{ $set: { meters: 6190 } },
116+
);
117+
console.log(`Updated ${res.result.n} documents`);
118+
} catch (err) {
119+
console.error(`Something went wrong: ${err}`);
120+
}
121+
}
122+
123+
For additional information, see the MDN documentation on
124+
:mdn:`await <Web/JavaScript/Reference/Operators/await>`.
125+
126+
127+
Callbacks
128+
---------
129+
130+
A callback is a method that gets called after another method has
131+
finished executing. This allows the enclosing method to continue to execute
132+
other commands until the original operation completes. Callbacks are often
133+
used to enforce the order of processing commands.
134+
135+
In the MongoDB Node.js driver, you can optionally declare a callback method to
136+
async operations that normally return Promises. Once the operation completes
137+
execution, the callback method executes as shown in the following code
138+
snippet:
139+
140+
.. code-block:: js
141+
142+
collection.findOneAndUpdate(
143+
{ name: "Barronette Peak" },
144+
{ $set: { name: "Baronette Peak" } },
145+
{},
146+
function(error, result) {
147+
if (!error) {
148+
console.log(`Operation completed successfully: ${result.ok}`);
149+
} else {
150+
console.log(`An error occurred: ${error}`);
151+
}
152+
},
153+
);
154+
155+
For more information on the callback method signature for the specific
156+
driver method, see the :node-api:`API documentation <>`.
157+
158+
.. note::
159+
160+
If you specify a callback, the method *does not* return a Promise.
161+
162+
Operational Considerations
163+
--------------------------
164+
165+
One common mistake when using ``async`` methods is to forget to use ``await``
166+
operator on Promises to get the value of the result rather than the Promise
167+
object. Consider the following example in which we iterate over a cursor
168+
using ``hasNext()``, which returns a Promise that resolves to a boolean that
169+
indicates whether additional results exist, and ``next()`` which returns a
170+
Promise that resolves to the next entry the cursor is pointing to.
171+
172+
.. code-block:: js
173+
174+
async function run() {
175+
...
176+
// WARNING: this snippet may cause an infinite loop
177+
const cursor = collection.find();
178+
179+
while (cursor.hasNext()) {
180+
console.log(cursor.next());
181+
}
182+
}
183+
184+
Since the call to ``hasNext()`` returns a ``Promise``, the conditional
185+
statement returns ``true`` regardless of the value that it resolves to.
186+
187+
If we alter the code to ``await`` the call to ``next()`` only, as demonstrated
188+
in the following code snippet, it throws the following error:
189+
``MongoError: Cursor is closed``.
190+
191+
.. code-block:: js
192+
193+
async function run() {
194+
...
195+
// WARNING: this snippet throws a MongoError
196+
const cursor = collection.find();
197+
198+
while (cursor.hasNext()) {
199+
console.log(await cursor.next());
200+
}
201+
}
202+
203+
While ``hasNext()`` is not called until after the result of ``next()`` returns,
204+
the call to ``hasNext()`` returns a Promise which evaluates to ``true`` rather
205+
than the value it resolves to, similar to the prior example. The code
206+
attempts to call ``next()`` on a Cursor that has already returned its results
207+
and closed as a result.
208+
209+
If we alter the code to only ``await`` the call to ``hasNext()`` as shown in
210+
the following example, the console prints Promise objects rather than the
211+
document objects.
212+
213+
.. code-block:: js
214+
215+
async function run() {
216+
...
217+
// WARNING: this snippet prints Promises instead of the objects they resolve to
218+
const cursor = collection.find();
219+
220+
while (await cursor.hasNext()) {
221+
console.log(cursor.next());
222+
}
223+
}
224+
225+
Use ``await`` before both the ``hasNext()`` and ``next()`` method calls to
226+
ensure that you are operating on the correct return values as demonstrated
227+
in the following code:
228+
229+
.. code-block:: js
230+
231+
async function run() {
232+
...
233+
const cursor = collection.find();
234+
235+
while (await cursor.hasNext()) {
236+
console.log(await cursor.next());
237+
}
238+
}
239+
240+
.. note::
241+
242+
For additional information on using Promises and Callbacks with the MongoDB
243+
Node.js driver, see this MongoDB University course video on `asynchronous
244+
Javascript programming <https://youtu.be/R4AEyKehpss>`_.

source/index.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ material on using the Node.js driver for the following:
6666

6767
* :doc:`CRUD Operations </fundamentals/crud>`: read and write data to MongoDB
6868

69+
* :doc:`Promises and Callbacks </fundamentals/promises>`: access return
70+
values using asynchronous Javascript
71+
6972
* :doc:`Indexes </fundamentals/indexes>`: create and design indexes to make
7073
your queries efficient
7174

0 commit comments

Comments
 (0)