Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 18 additions & 20 deletions 1-js/11-async/08-async-await/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ async function f() {

The word "async" before a function means one simple thing: a function always returns a promise. Other values are wrapped in a resolved promise automatically.

For instance, this function returns a resolved promise with the result of `1`, let's test it:
For instance, this function returns a resolved promise with the result of `1`; let's test it:

```js run
async function f() {
Expand All @@ -24,7 +24,7 @@ async function f() {
f().then(alert); // 1
```

...We could explicitly return a promise, that would be the same:
...We could explicitly return a promise, which would be the same:

```js run
async function f() {
Expand Down Expand Up @@ -67,7 +67,7 @@ f();

The function execution "pauses" at the line `(*)` and resumes when the promise settles, with `result` becoming its result. So the code above shows "done!" in one second.

Let's emphasize: `await` literally makes JavaScript wait until the promise settles, and then go on with the result. That doesn't cost any CPU resources, because the engine can do other jobs meanwhile: execute other scripts, handle events etc.
Let's emphasize: `await` literally makes JavaScript wait until the promise settles, and then go on with the result. That doesn't cost any CPU resources, because the engine can do other jobs in the meantime: execute other scripts, handle events, etc.

It's just a more elegant syntax of getting the promise result than `promise.then`, easier to read and write.

Expand Down Expand Up @@ -130,7 +130,7 @@ let response = await fetch('/article/promise-chaining/user.json');
let user = await response.json();
```

We can wrap it into an anonymous async function, like this:
But we can wrap it into an anonymous async function, like this:

```js
(async () => {
Expand All @@ -143,9 +143,9 @@ We can wrap it into an anonymous async function, like this:

````
````smart header="`await` accepts \"thenables\""
Like `promise.then`, `await` allows to use thenable objects (those with a callable `then` method). The idea is that a 3rd-party object may not be a promise, but promise-compatible: if it supports `.then`, that's enough to use with `await`.
Like `promise.then`, `await` allows to use thenable objects (those with a callable `then` method). The idea is that a third-party object may not be a promise, but promise-compatible: if it supports `.then`, that's enough to use with `await`.

Here's a demo `Thenable` class, the `await` below accepts its instances:
Here's a demo `Thenable` class; the `await` below accepts its instances:

```js run
class Thenable {
Expand All @@ -168,7 +168,7 @@ async function f() {
f();
```

If `await` gets a non-promise object with `.then`, it calls that method providing built-in functions `resolve`, `reject` as arguments (just as it does for a regular `Promise` executor). Then `await` waits until one of them is called (in the example above it happens in the line `(*)`) and then proceeds with the result.
If `await` gets a non-promise object with `.then`, it calls that method providing the built-in functions `resolve` and `reject` as arguments (just as it does for a regular `Promise` executor). Then `await` waits until one of them is called (in the example above it happens in the line `(*)`) and then proceeds with the result.
````

````smart header="Async class methods"
Expand All @@ -192,7 +192,7 @@ The meaning is the same: it ensures that the returned value is a promise and ena
````
## Error handling

If a promise resolves normally, then `await promise` returns the result. But in case of a rejection, it throws the error, just as if there were a `throw` statement at that line.
If a promise resolves normally, then `await promise` returns the result. But in the case of a rejection, it throws the error, just as if there were a `throw` statement at that line.

This code:

Expand All @@ -204,7 +204,7 @@ async function f() {
}
```

...Is the same as this:
...is the same as this:

```js
async function f() {
Expand Down Expand Up @@ -233,7 +233,7 @@ async function f() {
f();
```

In case of an error, the control jumps to the `catch` block. We can also wrap multiple lines:
In the case of an error, the control jumps to the `catch` block. We can also wrap multiple lines:

```js run
async function f() {
Expand Down Expand Up @@ -263,15 +263,13 @@ f().catch(alert); // TypeError: failed to fetch // (*)
*/!*
```

If we forget to add `.catch` there, then we get an unhandled promise error (viewable in the console). We can catch such errors using a global event handler as described in the chapter <info:promise-error-handling>.
If we forget to add `.catch` there, then we get an unhandled promise error (viewable in the console). We can catch such errors using a global `unhandledrejection` event handler as described in the chapter <info:promise-error-handling>.


```smart header="`async/await` and `promise.then/catch`"
When we use `async/await`, we rarely need `.then`, because `await` handles the waiting for us. And we can use a regular `try..catch` instead of `.catch`. That's usually (not always) more convenient.
When we use `async/await`, we rarely need `.then`, because `await` handles the waiting for us. And we can use a regular `try..catch` instead of `.catch`. That's usually (but not always) more convenient.

But at the top level of the code, when we're outside of any `async` function, we're syntactically unable to use `await`, so it's a normal practice to add `.then/catch` to handle the final result or falling-through errors.

Like in the line `(*)` of the example above.
But at the top level of the code, when we're outside any `async` function, we're syntactically unable to use `await`, so it's a normal practice to add `.then/catch` to handle the final result or falling-through error, like in the line `(*)` of the example above.
```

````smart header="`async/await` works well with `Promise.all`"
Expand All @@ -286,7 +284,7 @@ let results = await Promise.all([
]);
```

In case of an error, it propagates as usual: from the failed promise to `Promise.all`, and then becomes an exception that we can catch using `try..catch` around the call.
In the case of an error, it propagates as usual, from the failed promise to `Promise.all`, and then becomes an exception that we can catch using `try..catch` around the call.

````

Expand All @@ -295,13 +293,13 @@ In case of an error, it propagates as usual: from the failed promise to `Promise
The `async` keyword before a function has two effects:

1. Makes it always return a promise.
2. Allows to use `await` in it.
2. Allows `await` to be used in it.

The `await` keyword before a promise makes JavaScript wait until that promise settles, and then:

1. If it's an error, the exception is generated, same as if `throw error` were called at that very place.
1. If it's an error, the exception is generated same as if `throw error` were called at that very place.
2. Otherwise, it returns the result.

Together they provide a great framework to write asynchronous code that is easy both to read and write.
Together they provide a great framework to write asynchronous code that is easy to both read and write.

With `async/await` we rarely need to write `promise.then/catch`, but we still shouldn't forget that they are based on promises, because sometimes (e.g. in the outermost scope) we have to use these methods. Also `Promise.all` is a nice thing to wait for many tasks simultaneously.
With `async/await` we rarely need to write `promise.then/catch`, but we still shouldn't forget that they are based on promises, because sometimes (e.g. in the outermost scope) we have to use these methods. Also `Promise.all` is nice when we are waiting for many tasks simultaneously.