Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: rework several code examples that still use callbacks #13635

Merged
merged 3 commits into from
Jul 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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