Skip to content

Commit

Permalink
Merge pull request #13635 from Automattic/vkarpov15/gh-13616
Browse files Browse the repository at this point in the history
docs: rework several code examples that still use callbacks
  • Loading branch information
vkarpov15 committed Jul 20, 2023
2 parents 587983e + d37a754 commit d246223
Show file tree
Hide file tree
Showing 6 changed files with 27 additions and 87 deletions.
24 changes: 2 additions & 22 deletions docs/async-await.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,7 @@ This is especially helpful for avoiding callback hell when executing multiple as
Each of the three functions below retrieves a record from the database, updates it, and prints the updated record to the console.

```javascript
// Works.
function callbackUpdate() {
MyModel.findOne({ firstName: 'franklin', lastName: 'roosevelt' }, function(err, doc) {
if (err) {
handleError(err);
}

doc.middleName = 'delano';

doc.save(function(err, updatedDoc) {
if (err) {
handleError(err);
}

// Final logic is 2 callbacks deep
console.log(updatedDoc);
});
});
}

// Better.
// Using promise chaining
function thenUpdate() {
MyModel.findOne({ firstName: 'franklin', lastName: 'roosevelt' })
.then(function(doc) {
Expand All @@ -44,7 +24,7 @@ function thenUpdate() {
});
}

// Best?
// Using async/await
async function awaitUpdate() {
try {
const doc = await MyModel.findOne({
Expand Down
24 changes: 7 additions & 17 deletions docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,8 @@ const schema = new mongoose.Schema({
});
const Model = db.model('Test', schema);

Model.create([{ name: 'Val' }, { name: 'Val' }], function(err) {
console.log(err); // No error, unless index was already built
});
// No error, unless index was already built
await Model.create([{ name: 'Val' }, { name: 'Val' }]);
```

However, if you wait for the index to build using the `Model.on('index')` event, attempts to save duplicates will correctly error.
Expand All @@ -92,21 +91,12 @@ const schema = new mongoose.Schema({
});
const Model = db.model('Test', schema);

Model.on('index', function(err) { // <-- Wait for model's indexes to finish
assert.ifError(err);
Model.create([{ name: 'Val' }, { name: 'Val' }], function(err) {
console.log(err);
});
});

// Promise based alternative. `init()` returns a promise that resolves
// when the indexes have finished building successfully. The `init()`
// Wait for model's indexes to finish. The `init()`
// function is idempotent, so don't worry about triggering an index rebuild.
Model.init().then(function() {
Model.create([{ name: 'Val' }, { name: 'Val' }], function(err) {
console.log(err);
});
});
await Model.init();

// Throws a duplicate key error
await Model.create([{ name: 'Val' }, { name: 'Val' }]);
```

MongoDB persists indexes, so you only need to rebuild indexes if you're starting
Expand Down
9 changes: 4 additions & 5 deletions docs/middleware.md
Original file line number Diff line number Diff line change
Expand Up @@ -532,11 +532,10 @@ schema.post('update', function(error, res, next) {
});

const people = [{ name: 'Axl Rose' }, { name: 'Slash' }];
Person.create(people, function(error) {
Person.update({ name: 'Slash' }, { $set: { name: 'Axl Rose' } }, function(error) {
// `error.message` will be "There was a duplicate key error"
});
});
await Person.create(people);

// Throws "There was a duplicate key error"
await Person.update({ name: 'Slash' }, { $set: { name: 'Axl Rose' } });
```

Error handling middleware can transform an error, but it can't remove the
Expand Down
42 changes: 7 additions & 35 deletions docs/queries.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,44 +104,16 @@ A full list of [Query helper functions can be found in the API docs](api/query.h
</a>
</h2>

Mongoose queries are **not** promises. They have a `.then()`
function for [co](https://www.npmjs.com/package/co) and
[async/await](http://thecodebarbarian.com/common-async-await-design-patterns-in-node.js.html)
as a convenience. However, unlike promises, calling a query's `.then()`
can execute the query multiple times.

For example, the below code will execute 3 `updateMany()` calls, one
because of the callback, and two because `.then()` is called twice.
Mongoose queries are **not** promises.
Queries are [thenables](https://masteringjs.io/tutorials/fundamentals/thenable), meaning they have a `.then()` method for [async/await](http://thecodebarbarian.com/common-async-await-design-patterns-in-node.js.html) as a convenience.
However, unlike promises, calling a query's `.then()` executes the query, so calling `then()` multiple times will throw an error.

```javascript
const q = MyModel.updateMany({}, { isDeleted: true }, function() {
console.log('Update 1');
});

q.then(() => console.log('Update 2'));
q.then(() => console.log('Update 3'));
```
const q = MyModel.updateMany({}, { isDeleted: true });

Don't mix using callbacks and promises with queries, or you may end up
with duplicate operations. That's because passing a callback to a query function
immediately executes the query, and calling [`then()`](https://masteringjs.io/tutorials/fundamentals/then)
executes the query again.

Mixing promises and callbacks can lead to duplicate entries in arrays.
For example, the below code inserts 2 entries into the `tags` array, **not** just 1.

```javascript
const BlogPost = mongoose.model('BlogPost', new Schema({
title: String,
tags: [String]
}));

// Because there's both `await` **and** a callback, this `updateOne()` executes twice
// and thus pushes the same string into `tags` twice.
const update = { $push: { tags: ['javascript'] } };
await BlogPost.updateOne({ title: 'Introduction to Promises' }, update, (err, res) => {
console.log(res);
});
await q.then(() => console.log('Update 2'));
// Throws "Query was already executed: Test.updateMany({}, { isDeleted: true })"
await q.then(() => console.log('Update 3'));
```

<h2 id="refs"><a href="#refs">References to other documents</a></h2>
Expand Down
6 changes: 4 additions & 2 deletions lib/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -3045,8 +3045,10 @@ Model.startSession = function() {
*
* #### Example:
*
* const arr = [{ name: 'Star Wars' }, { name: 'The Empire Strikes Back' }];
* Movies.insertMany(arr, function(error, docs) {});
* await Movies.insertMany([
* { name: 'Star Wars' },
* { name: 'The Empire Strikes Back' }
* ]);
*
* @param {Array|Object|*} doc(s)
* @param {Object} [options] see the [mongodb driver options](https://mongodb.github.io/node-mongodb-native/4.9/classes/Collection.html#insertMany)
Expand Down
9 changes: 3 additions & 6 deletions test/model.insertMany.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ describe('insertMany()', function() {
const Movie = db.model('Movie', schema);
const start = Date.now();

const arr = [{ name: 'Star Wars' }, { name: 'The Empire Strikes Back' }];
return Movie.insertMany(arr).
return Movie.insertMany([{ name: 'Star Wars' }, { name: 'The Empire Strikes Back' }]).
then(docs => {
assert.equal(docs.length, 2);
assert.ok(!docs[0].isNew);
Expand Down Expand Up @@ -93,8 +92,7 @@ describe('insertMany()', function() {
}, { timestamps: true });
const Movie = db.model('Movie', schema);

const arr = [{ name: 'Star Wars' }, { name: 'The Empire Strikes Back' }];
let docs = await Movie.insertMany(arr);
let docs = await Movie.insertMany([{ name: 'Star Wars' }, { name: 'The Empire Strikes Back' }]);
assert.equal(docs.length, 2);
assert.ok(!docs[0].isNew);
assert.ok(!docs[1].isNew);
Expand Down Expand Up @@ -299,8 +297,7 @@ describe('insertMany()', function() {
});
const Movie = db.model('Movie', schema);

const arr = [{ name: 'Star Wars' }, { name: 'The Empire Strikes Back' }];
let docs = await Movie.insertMany(arr);
let docs = await Movie.insertMany([{ name: 'Star Wars' }, { name: 'The Empire Strikes Back' }]);
assert.equal(docs.length, 2);
assert.equal(calledPre, 2);
assert.equal(calledPost, 1);
Expand Down

0 comments on commit d246223

Please sign in to comment.