Merge branch 'master' into patch-2
This commit is contained in:
commit
b9bb854c73
84 changed files with 713 additions and 694 deletions
|
@ -109,7 +109,7 @@ loadScript('https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.2.0/lodash.js', s
|
|||
|
||||
That's called a "callback-based" style of asynchronous programming. A function that does something asynchronously should provide a `callback` argument where we put the function to run after it's complete.
|
||||
|
||||
Here we did it in `loadScript`, but of course, it's a general approach.
|
||||
Here we did it in `loadScript`, but of course it's a general approach.
|
||||
|
||||
## Callback in callback
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ Everyone is happy: you, because the people don't crowd you anymore, and fans, be
|
|||
|
||||
This is a real-life analogy for things we often have in programming:
|
||||
|
||||
1. A "producing code" that does something and takes time. For instance, a code that loads the data over a network. That's a "singer".
|
||||
1. A "producing code" that does something and takes time. For instance, some code that loads the data over a network. That's a "singer".
|
||||
2. A "consuming code" that wants the result of the "producing code" once it's ready. Many functions may need that result. These are the "fans".
|
||||
3. A *promise* is a special JavaScript object that links the "producing code" and the "consuming code" together. In terms of our analogy: this is the "subscription list". The "producing code" takes whatever time it needs to produce the promised result, and the "promise" makes that result available to all of the subscribed code when it's ready.
|
||||
|
||||
|
@ -22,7 +22,7 @@ let promise = new Promise(function(resolve, reject) {
|
|||
});
|
||||
```
|
||||
|
||||
The function passed to `new Promise` is called the *executor*. When `new Promise` is created, it runs automatically. It contains the producing code, that should eventually produce a result. In terms of the analogy above, the executor is the "singer".
|
||||
The function passed to `new Promise` is called the *executor*. When `new Promise` is created, the executor runs automatically. It contains the producing code which should eventually produce the result. In terms of the analogy above: the executor is the "singer".
|
||||
|
||||
Its arguments `resolve` and `reject` are callbacks provided by JavaScript itself. Our code is only inside the executor.
|
||||
|
||||
|
@ -31,7 +31,7 @@ When the executor obtains the result, be it soon or late - doesn't matter, it sh
|
|||
- `resolve(value)` — if the job finished successfully, with result `value`.
|
||||
- `reject(error)` — if an error occurred, `error` is the error object.
|
||||
|
||||
So to summarize: the executor runs automatically and attempts to perform a job. When it is finished with the attempt it calls `resolve` if it was succssful or `reject` if there was an error.
|
||||
So to summarize: the executor runs automatically and performs a job. Then it should call `resolve` if it was succssful or `reject` if there was an error.
|
||||
|
||||
The `promise` object returned by the `new Promise` constructor has internal properties:
|
||||
|
||||
|
@ -272,6 +272,10 @@ let promise = new Promise(resolve => resolve("done!"));
|
|||
|
||||
promise.then(alert); // done! (shows up right now)
|
||||
```
|
||||
|
||||
Note that this is different, and more powerful than the real life "subscription list" scenario. If the singer has already released their song and then a person signs up on the subscription list, they probably won't receive that song. Subscriptions in real life must be done prior to the event.
|
||||
|
||||
Promises are more flexible. We can add handlers any time: if the result is already there, our handlers get it immediately.
|
||||
````
|
||||
|
||||
Next, let's see more practical examples of how promises can help us write asynchronous code.
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
# Promises chaining
|
||||
|
||||
Let's return to the problem mentioned in the chapter <info:callbacks>: we have a sequence of asynchronous tasks to be done one after another. For instance, loading scripts. How can we code it well?
|
||||
Let's return to the problem mentioned in the chapter <info:callbacks>: we have a sequence of asynchronous tasks to be performed one after another — for instance, loading scripts. How can we code it well?
|
||||
|
||||
Promises provide a couple of recipes to do that.
|
||||
|
||||
|
@ -72,7 +72,7 @@ promise.then(function(result) {
|
|||
});
|
||||
```
|
||||
|
||||
What we did here is just several handlers to one promise. They don't pass the result to each other, instead they process it independently.
|
||||
What we did here is just several handlers to one promise. They don't pass the result to each other; instead they process it independently.
|
||||
|
||||
Here's the picture (compare it with the chaining above):
|
||||
|
||||
|
@ -164,7 +164,7 @@ loadScript("/article/promise-chaining/one.js")
|
|||
|
||||
Here each `loadScript` call returns a promise, and the next `.then` runs when it resolves. Then it initiates the loading of the next script. So scripts are loaded one after another.
|
||||
|
||||
We can add more asynchronous actions to the chain. Please note that the code is still "flat", it grows down, not to the right. There are no signs of "pyramid of doom".
|
||||
We can add more asynchronous actions to the chain. Please note that the code is still "flat" — it grows down, not to the right. There are no signs of the "pyramid of doom".
|
||||
|
||||
Technically, we could add `.then` directly to each `loadScript`, like this:
|
||||
|
||||
|
@ -287,7 +287,7 @@ fetch('/article/promise-chaining/user.json')
|
|||
});
|
||||
```
|
||||
|
||||
The code works, see comments about the details. However, there's a potential problem in it, a typical error of those who begin to use promises.
|
||||
The code works; see comments about the details. However, there's a potential problem in it, a typical error for those who begin to use promises.
|
||||
|
||||
Look at the line `(*)`: how can we do something *after* the avatar has finished showing and gets removed? For instance, we'd like to show a form for editing that user or something else. As of now, there's no way.
|
||||
|
||||
|
@ -319,13 +319,9 @@ fetch('/article/promise-chaining/user.json')
|
|||
.then(githubUser => alert(`Finished showing ${githubUser.name}`));
|
||||
```
|
||||
|
||||
That is, `.then` handler in line `(*)` now returns `new Promise`, that becomes settled only after the call of `resolve(githubUser)` in `setTimeout` `(**)`.
|
||||
That is, the `.then` handler in line `(*)` now returns `new Promise`, that becomes settled only after the call of `resolve(githubUser)` in `setTimeout` `(**)`. The next `.then` in the chain will wait for that.
|
||||
|
||||
The next `.then` in chain will wait for that.
|
||||
|
||||
As a good practice, an asynchronous action should always return a promise.
|
||||
|
||||
That makes it possible to plan actions after it. Even if we don't plan to extend the chain now, we may need it later.
|
||||
As a good practice, an asynchronous action should always return a promise. That makes it possible to plan actions after it; even if we don't plan to extend the chain now, we may need it later.
|
||||
|
||||
Finally, we can split the code into reusable functions:
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@ new Promise((resolve, reject) => {
|
|||
new Promise((resolve, reject) => {
|
||||
*!*
|
||||
reject(new Error("Whoops!"));
|
||||
*/!*
|
||||
*/!*
|
||||
}).catch(alert); // Error: Whoops!
|
||||
```
|
||||
|
||||
|
@ -98,7 +98,7 @@ The final `.catch` not only catches explicit rejections, but also occasional err
|
|||
|
||||
As we already noticed, `.catch` at the end of the chain is similar to `try..catch`. We may have as many `.then` handlers as we want, and then use a single `.catch` at the end to handle errors in all of them.
|
||||
|
||||
In a regular `try..catch` we can analyze the error and maybe rethrow it if can't handle. The same thing is possible for promises.
|
||||
In a regular `try..catch` we can analyze the error and maybe rethrow it if it can't be handled. The same thing is possible for promises.
|
||||
|
||||
If we `throw` inside `.catch`, then the control goes to the next closest error handler. And if we handle the error and finish normally, then it continues to the closest successful `.then` handler.
|
||||
|
||||
|
|
|
@ -4,9 +4,9 @@ There are 5 static methods in the `Promise` class. We'll quickly cover their use
|
|||
|
||||
## Promise.all
|
||||
|
||||
Let's say we want to run many promises to execute in parallel, and wait until all of them are ready.
|
||||
Let's say we want many promises to execute in parallel and wait until all of them are ready.
|
||||
|
||||
For instance, download several URLs in parallel and process the content when all are done.
|
||||
For instance, download several URLs in parallel and process the content once they are all done.
|
||||
|
||||
That's what `Promise.all` is for.
|
||||
|
||||
|
@ -18,7 +18,7 @@ let promise = Promise.all([...promises...]);
|
|||
|
||||
`Promise.all` takes an array of promises (it technically can be any iterable, but is usually an array) and returns a new promise.
|
||||
|
||||
The new promise resolves when all listed promises are settled and the array of their results becomes its result.
|
||||
The new promise resolves when all listed promises are settled, and the array of their results becomes its result.
|
||||
|
||||
For instance, the `Promise.all` below settles after 3 seconds, and then its result is an array `[1, 2, 3]`:
|
||||
|
||||
|
@ -89,12 +89,12 @@ Promise.all([
|
|||
]).catch(alert); // Error: Whoops!
|
||||
```
|
||||
|
||||
Here the second promise rejects in two seconds. That leads to an immediate rejection of `Promise.all`, so `.catch` executes: the rejection error becomes the outcome of the whole `Promise.all`.
|
||||
Here the second promise rejects in two seconds. That leads to an immediate rejection of `Promise.all`, so `.catch` executes: the rejection error becomes the outcome of the entire `Promise.all`.
|
||||
|
||||
```warn header="In case of an error, other promises are ignored"
|
||||
If one promise rejects, `Promise.all` immediately rejects, completely forgetting about the other ones in the list. Their results are ignored.
|
||||
|
||||
For example, if there are multiple `fetch` calls, like in the example above, and one fails, other ones will still continue to execute, but `Promise.all` won't watch them anymore. They will probably settle, but the result will be ignored.
|
||||
For example, if there are multiple `fetch` calls, like in the example above, and one fails, the others will still continue to execute, but `Promise.all` won't watch them anymore. They will probably settle, but their results will be ignored.
|
||||
|
||||
`Promise.all` does nothing to cancel them, as there's no concept of "cancellation" in promises. In [another chapter](info:fetch-abort) we'll cover `AbortController` that can help with that, but it's not a part of the Promise API.
|
||||
```
|
||||
|
@ -110,7 +110,7 @@ Promise.all([
|
|||
setTimeout(() => resolve(1), 1000)
|
||||
}),
|
||||
2,
|
||||
3
|
||||
3
|
||||
]).then(alert); // 1, 2, 3
|
||||
```
|
||||
|
||||
|
@ -121,7 +121,7 @@ So we are able to pass ready values to `Promise.all` where convenient.
|
|||
|
||||
[recent browser="new"]
|
||||
|
||||
`Promise.all` rejects as a whole if any promise rejects. That's good for "all or nothing" cases, when we need *all* results to go on:
|
||||
`Promise.all` rejects as a whole if any promise rejects. That's good for "all or nothing" cases, when we need *all* results successful to proceed:
|
||||
|
||||
```js
|
||||
Promise.all([
|
||||
|
@ -131,7 +131,7 @@ Promise.all([
|
|||
]).then(render); // render method needs results of all fetches
|
||||
```
|
||||
|
||||
`Promise.allSettled` waits for all promises to settle. The resulting array has:
|
||||
`Promise.allSettled` just waits for all promises to settle, regardless of the result. The resulting array has:
|
||||
|
||||
- `{status:"fulfilled", value:result}` for successful responses,
|
||||
- `{status:"rejected", reason:error}` for errors.
|
||||
|
@ -169,7 +169,7 @@ The `results` in the line `(*)` above will be:
|
|||
]
|
||||
```
|
||||
|
||||
So, for each promise we get its status and `value/error`.
|
||||
So for each promise we get its status and `value/error`.
|
||||
|
||||
### Polyfill
|
||||
|
||||
|
@ -197,7 +197,7 @@ Now we can use `Promise.allSettled` to get the results of *all* given promises,
|
|||
|
||||
## Promise.race
|
||||
|
||||
Similar to `Promise.all`, but waits only for the first settled promise, and gets its result (or error).
|
||||
Similar to `Promise.all`, but waits only for the first settled promise and gets its result (or error).
|
||||
|
||||
The syntax is:
|
||||
|
||||
|
@ -222,9 +222,11 @@ The first promise here was fastest, so it became the result. After the first set
|
|||
|
||||
Methods `Promise.resolve` and `Promise.reject` are rarely needed in modern code, because `async/await` syntax (we'll cover it [a bit later](info:async-await)) makes them somewhat obsolete.
|
||||
|
||||
We cover them here for completeness, and for those who can't use `async/await` for some reason.
|
||||
We cover them here for completeness and for those who can't use `async/await` for some reason.
|
||||
|
||||
- `Promise.resolve(value)` creates a resolved promise with the result `value`.
|
||||
### Promise.resolve
|
||||
|
||||
`Promise.resolve(value)` creates a resolved promise with the result `value`.
|
||||
|
||||
Same as:
|
||||
|
||||
|
@ -234,7 +236,7 @@ let promise = new Promise(resolve => resolve(value));
|
|||
|
||||
The method is used for compatibility, when a function is expected to return a promise.
|
||||
|
||||
For example, `loadCached` function below fetches a URL and remembers (caches) its content. For future calls with the same URL it immediately gets the previous content from cache, but uses `Promise.resolve` to make a promise of it, so that the returned value is always a promise:
|
||||
For example, the `loadCached` function below fetches a URL and remembers (caches) its content. For future calls with the same URL it immediately gets the previous content from cache, but uses `Promise.resolve` to make a promise of it, so the returned value is always a promise:
|
||||
|
||||
```js
|
||||
let cache = new Map();
|
||||
|
@ -259,7 +261,7 @@ We can write `loadCached(url).then(…)`, because the function is guaranteed to
|
|||
|
||||
### Promise.reject
|
||||
|
||||
- `Promise.reject(error)` creates a rejected promise with `error`.
|
||||
`Promise.reject(error)` creates a rejected promise with `error`.
|
||||
|
||||
Same as:
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
# Promisification
|
||||
|
||||
Promisification -- is a long word for a simple transform. It's conversion of a function that accepts a callback into a function returning a promise.
|
||||
"Promisification" is a long word for a simple transformation. It's the conversion of a function that accepts a callback into a function that returns a promise.
|
||||
|
||||
Such transforms are often needed in real-life, as many functions and libraries are callback-based. But promises are more convenient. So it makes sense to promisify those.
|
||||
Such transformations are often required in real-life, as many functions and libraries are callback-based. But promises are more convenient, so it makes sense to promisify them.
|
||||
|
||||
For instance, we have `loadScript(src, callback)` from the chapter <info:callbacks>.
|
||||
|
||||
|
@ -21,7 +21,7 @@ function loadScript(src, callback) {
|
|||
// loadScript('path/script.js', (err, script) => {...})
|
||||
```
|
||||
|
||||
Let's promisify it. The new `loadScriptPromise(src)` function will do the same, but accept only `src` (no `callback`) and return a promise.
|
||||
Let's promisify it. The new `loadScriptPromise(src)` function achieves the same result, but it accepts only `src` (no `callback`) and returns a promise.
|
||||
|
||||
```js
|
||||
let loadScriptPromise = function(src) {
|
||||
|
@ -41,9 +41,7 @@ Now `loadScriptPromise` fits well in promise-based code.
|
|||
|
||||
As we can see, it delegates all the work to the original `loadScript`, providing its own callback that translates to promise `resolve/reject`.
|
||||
|
||||
In practice we'll probably need to promisify many functions, it makes sense to use a helper.
|
||||
|
||||
We'll call it `promisify(f)`: it accepts a to-promisify function `f` and returns a wrapper function.
|
||||
In practice we'll probably need to promisify many functions, so it makes sense to use a helper. We'll call it `promisify(f)`: it accepts a to-promisify function `f` and returns a wrapper function.
|
||||
|
||||
That wrapper does the same as in the code above: returns a promise and passes the call to the original `f`, tracking the result in a custom callback:
|
||||
|
||||
|
@ -103,7 +101,7 @@ f = promisify(f, true);
|
|||
f(...).then(arrayOfResults => ..., err => ...)
|
||||
```
|
||||
|
||||
For more exotic callback formats, like those without `err` at all: `callback(result)`, we can promisify such functions without using the helper, manually.
|
||||
For more exotic callback formats, like those without `err` at all: `callback(result)`, we can promisify such functions manually without using the helper.
|
||||
|
||||
There are also modules with a bit more flexible promisification functions, e.g. [es6-promisify](https://github.com/digitaldesignlabs/es6-promisify). In Node.js, there's a built-in `util.promisify` function for that.
|
||||
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
|
||||
Promise handlers `.then`/`.catch`/`.finally` are always asynchronous.
|
||||
|
||||
Even when a Promise is immediately resolved, the code on the lines *below* `.then`/`.catch`/`.finally` will still execute before these handlers .
|
||||
Even when a Promise is immediately resolved, the code on the lines *below* `.then`/`.catch`/`.finally` will still execute before these handlers.
|
||||
|
||||
Here's the demo:
|
||||
Here's a demo:
|
||||
|
||||
```js run
|
||||
let promise = Promise.resolve();
|
||||
|
@ -23,14 +23,14 @@ Why did the `.then` trigger afterwards? What's going on?
|
|||
|
||||
## Microtasks queue
|
||||
|
||||
Asynchronous tasks need proper management. For that, the standard specifies an internal queue `PromiseJobs`, more often referred to as "microtask queue" (v8 term).
|
||||
Asynchronous tasks need proper management. For that, the Ecma standard specifies an internal queue `PromiseJobs`, more often referred to as the "microtask queue" (ES8 term).
|
||||
|
||||
As said in the [specification](https://tc39.github.io/ecma262/#sec-jobs-and-job-queues):
|
||||
As stated in the [specification](https://tc39.github.io/ecma262/#sec-jobs-and-job-queues):
|
||||
|
||||
- The queue is first-in-first-out: tasks enqueued first are run first.
|
||||
- Execution of a task is initiated only when nothing else is running.
|
||||
|
||||
Or, to say that simply, when a promise is ready, its `.then/catch/finally` handlers are put into the queue. They are not executed yet. When the JavaScript engine becomes free from the current code, it takes a task from the queue and executes it.
|
||||
Or, to say more simply, when a promise is ready, its `.then/catch/finally` handlers are put into the queue; they are not executed yet. When the JavaScript engine becomes free from the current code, it takes a task from the queue and executes it.
|
||||
|
||||
That's why "code finished" in the example above shows first.
|
||||
|
||||
|
@ -38,7 +38,7 @@ That's why "code finished" in the example above shows first.
|
|||
|
||||
Promise handlers always go through this internal queue.
|
||||
|
||||
If there's a chain with multiple `.then/catch/finally`, then every one of them is executed asynchronously. That is, it first gets queued, and executed when the current code is complete and previously queued handlers are finished.
|
||||
If there's a chain with multiple `.then/catch/finally`, then every one of them is executed asynchronously. That is, it first gets queued, then executed when the current code is complete and previously queued handlers are finished.
|
||||
|
||||
**What if the order matters for us? How can we make `code finished` run after `promise done`?**
|
||||
|
||||
|
@ -58,7 +58,7 @@ Remember the `unhandledrejection` event from the chapter <info:promise-error-han
|
|||
|
||||
Now we can see exactly how JavaScript finds out that there was an unhandled rejection.
|
||||
|
||||
**"Unhandled rejection" occurs when a promise error is not handled at the end of the microtask queue.**
|
||||
**An "unhandled rejection" occurs when a promise error is not handled at the end of the microtask queue.**
|
||||
|
||||
Normally, if we expect an error, we add `.catch` to the promise chain to handle it:
|
||||
|
||||
|
@ -72,7 +72,7 @@ promise.catch(err => alert('caught'));
|
|||
window.addEventListener('unhandledrejection', event => alert(event.reason));
|
||||
```
|
||||
|
||||
...But if we forget to add `.catch`, then, after the microtask queue is empty, the engine triggers the event:
|
||||
But if we forget to add `.catch`, then, after the microtask queue is empty, the engine triggers the event:
|
||||
|
||||
```js run
|
||||
let promise = Promise.reject(new Error("Promise Failed!"));
|
||||
|
@ -93,20 +93,20 @@ setTimeout(() => promise.catch(err => alert('caught')), 1000);
|
|||
window.addEventListener('unhandledrejection', event => alert(event.reason));
|
||||
```
|
||||
|
||||
Now, if you run it, we'll see `Promise Failed!` first and then `caught`.
|
||||
Now, if we run it, we'll see `Promise Failed!` first and then `caught`.
|
||||
|
||||
If we didn't know about the microtasks queue, we could wonder: "Why did `unhandledrejection` handler run? We did catch the error!".
|
||||
If we didn't know about the microtasks queue, we could wonder: "Why did `unhandledrejection` handler run? We did catch and handle the error!"
|
||||
|
||||
But now we understand that `unhandledrejection` is generated when the microtask queue is complete: the engine examines promises and, if any of them is in "rejected" state, then the event triggers.
|
||||
But now we understand that `unhandledrejection` is generated when the microtask queue is complete: the engine examines promises and, if any of them is in the "rejected" state, then the event triggers.
|
||||
|
||||
In the example above, `.catch` added by `setTimeout` also triggers, but later, after `unhandledrejection` has already occurred, so that doesn't change anything.
|
||||
In the example above, `.catch` added by `setTimeout` also triggers. But it does so later, after `unhandledrejection` has already occurred, so it doesn't change anything.
|
||||
|
||||
## Summary
|
||||
|
||||
Promise handling is always asynchronous, as all promise actions pass through the internal "promise jobs" queue, also called "microtask queue" (v8 term).
|
||||
Promise handling is always asynchronous, as all promise actions pass through the internal "promise jobs" queue, also called "microtask queue" (ES8 term).
|
||||
|
||||
So, `.then/catch/finally` handlers are always called after the current code is finished.
|
||||
So `.then/catch/finally` handlers are always called after the current code is finished.
|
||||
|
||||
If we need to guarantee that a piece of code is executed after `.then/catch/finally`, we can add it into a chained `.then` call.
|
||||
|
||||
In most Javascript engines, including browsers and Node.js, the concept of microtasks is closely tied with "event loop" and "macrotasks". As these have no direct relation to promises, they are covered in another part of the tutorial, in the chapter <info:event-loop>.
|
||||
In most Javascript engines, including browsers and Node.js, the concept of microtasks is closely tied with the "event loop" and "macrotasks". As these have no direct relation to promises, they are covered in another part of the tutorial, in the chapter <info:event-loop>.
|
||||
|
|
|
@ -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() {
|
||||
|
@ -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() {
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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 () => {
|
||||
|
@ -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 {
|
||||
|
@ -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"
|
||||
|
@ -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:
|
||||
|
||||
|
@ -204,7 +204,7 @@ async function f() {
|
|||
}
|
||||
```
|
||||
|
||||
...Is the same as this:
|
||||
...is the same as this:
|
||||
|
||||
```js
|
||||
async function f() {
|
||||
|
@ -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() {
|
||||
|
@ -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`"
|
||||
|
@ -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.
|
||||
|
||||
````
|
||||
|
||||
|
@ -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.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue