Skip to content

Commit

Permalink
Merge branch 'master' of github.com:Automattic/mongoose
Browse files Browse the repository at this point in the history
  • Loading branch information
vkarpov15 committed Jul 30, 2019
2 parents 710935d + 1ceeb2b commit 1670ff1
Showing 1 changed file with 168 additions and 0 deletions.
168 changes: 168 additions & 0 deletions docs/async-await.pug
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
extends layout

append style
link(rel="stylesheet", href="/docs/css/inlinecpc.css")
script(type="text/javascript" src="/docs/js/native.js")

block content
:markdown
## Async/Await

<script>
_native.init("CK7DT53U",{
targetClass: 'native-inline'
});
</script>

<div class="native-inline">
<a href="#native_link#"><span class="sponsor">Sponsor</span> #native_company# — #native_desc#</a>
</div>

ul
li
a(href="#basic-use") Basic Use
li
a(href="#async-functions") Async Functions
li
a(href="#queries") Queries

h3(id="basic-use") Basic Use
:markdown
Async/await lets us write asynchronous code as if it were synchronous. This is especially helpful for avoiding callback hell when executing multiple async operations in sequence--a common scenario when working with Mongoose. 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.
function thenUpdate() {
MyModel.findOne({firstName: 'franklin', lastName: 'roosevelt'})
.then(function(doc) {
doc.middleName = 'delano';
return doc.save();
})
.then(console.log)
.catch(function(err) {
handleError(err);
});
};

// Best?
async function awaitUpdate() {
try {
const doc = await MyModel.findOne({
firstName: 'franklin',
lastName: 'roosevelt'
});

doc.middleName = 'delano';

console.log(await doc.save());
}

catch(err) {
handleError(err);
}
}
```
:markdown
Note that the specific fulfillment values of different Mongoose methods vary, and may be affected by configuration. Please refer to the [API documentation](./api.html) for information about specific methods.

h3(id="async-functions") Async Functions
:markdown
Adding the keyword *async* to a JavaScript function automatically causes it to return a native JavaScript promise. This is true [regardless of the return value we specify in the function body](http://thecodebarbarian.com/async-functions-in-javascript.html#an-async-function-always-returns-a-promise).

```javascript
async function getUser() {
//Inside getUser, we can await an async operation and interact with
//foundUser as a normal, non-promise value...
const foundUser = await User.findOne({name: 'bill'});

console.log(foundUser); //Prints '{name: 'bill', admin: false}'
return foundUser;
}

//However, because async functions always return a promise,
//user is a promise.
const user = getUser();

console.log(user) //Oops. Prints '[Promise]'
```
:markdown
Instead, treat the return value of an async function as you would any other promise. Await its fulfillment inside another async function, or chain onto it using ‘.then’ blocks.

```javascript
async function getUser() {
const foundUser = await User.findOne({name: 'bill'});
return foundUser;
};

async function doStuffWithUser() {
//Await the promise returned from calling getUser.
const user = await getUser();

console.log(user); //Prints '{name: 'bill', admin: false}'
}

h3(id="queries") Async/Await with Mongoose Queries
:markdown
Under the hood, [*async/await* is syntactic sugar](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await)
over the Promise API. Due to the surprisingly simple way promises are implemented in JavaScript, the keyword *await* will try to unwrap any object with a property whose key is the string ‘then’ and whose value is a function. Such objects belong to a broader class of objects called thenables. If the thenable being unwrapped is a genuine promise, e.g. an instance of the [Promise constructor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise), we enjoy several guarantees about how the object’s ‘then’ function will behave. However, Mongoose provides several static helper methods that return a different class of thenable object called a [Query](./queries.html)--and [Queries are not promises](./queries.html#queries-are-not-promises). Because Queries are also *thenables*, we can interact with a Query using async/await just as we would interact with a genuine promise, with one key difference: observing the fulfillment value of a genuine promise cannot under any circumstances change that value, but trying to re-observe the value of a Query may cause the Query to be re-executed.

```javascript
function isPromise(thenable) {
return thenable instanceof Promise;
};

// The fulfillment value of the promise returned by user.save() will always be the same,
// regardless of how, or how often, we observe it.
async function observePromise() {
const user = await User.findOne({firstName: 'franklin', lastName:'roosevelt'});

user.middleName = 'delano';

// Document.prototype.save() returns a *genuine* promise
const realPromise = user.save();

console.log(isPromise(realPromise)); //true

const awaitedValue = await realPromise;

realPromise.then(chainedValue => console.log(chainedValue === awaitedValue)); //true
}

// By contrast, the value we receive when we try to observe the same Query more than
// once is different every time. The Query is re-executing.
async function observeQuery() {
const query = User.findOne({firstname: 'leroy', lastName: 'jenkins'});

console.log(isPromise(query)); //false

const awaitedValue = await query;

query.then(chainedValue => console.log(chainedValue === awaitedValue)); //false
}
```
:markdown
You are most likely to accidentally re-execute queries in this way when mixing callbacks with async/await. This is never necessary and should be avoided. If you need a Query to return a fully-fleged promise instead of a thenable, you can use [Query#exec()](./api/query.html#query_Query-exec).



0 comments on commit 1670ff1

Please sign in to comment.