Skip to content

Commit ba3d996

Browse files
committed
Update with respect to en.javascript.info
1 parent 99fc7bb commit ba3d996

File tree

6 files changed

+32
-196
lines changed

6 files changed

+32
-196
lines changed

1-js/11-async/03-promise-chaining/01-then-vs-catch/solution.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
The short answer is: **no, they are not the equal**:
1+
The short answer is: **no, they are not equal**:
22

33
The difference is that if an error happens in `f1`, then it is handled by `.catch` here:
44

@@ -17,4 +17,4 @@ promise
1717

1818
That's because an error is passed down the chain, and in the second code piece there's no chain below `f1`.
1919

20-
In other words, `.then` passes results/errors to the next `.then/catch`. So in the first example, there's a `catch` below, and in the second one -- there isn't, so the error is unhandled.
20+
In other words, `.then` passes results/errors to the next `.then/catch`. So in the first example, there's a `catch` below, and in the second one there isn't, so the error is unhandled.

1-js/11-async/03-promise-chaining/article.md

Lines changed: 27 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
# Promises chaining
33

4-
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?
4+
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?
55

66
Promises provide a couple of recipes to do that.
77

@@ -48,24 +48,6 @@ The whole thing works, because a call to `promise.then` returns a promise, so th
4848

4949
When a handler returns a value, it becomes the result of that promise, so the next `.then` is called with it.
5050

51-
To make these words more clear, here's the start of the chain:
52-
53-
```js run
54-
new Promise(function(resolve, reject) {
55-
56-
setTimeout(() => resolve(1), 1000);
57-
58-
}).then(function(result) {
59-
60-
alert(result);
61-
return result * 2; // <-- (1)
62-
63-
}) // <-- (2)
64-
// .then…
65-
```
66-
67-
The value returned by `.then` is a promise, that's why we are able to add another `.then` at `(2)`. When the value is returned in `(1)`, that promise becomes resolved, so the next handler runs with the value.
68-
6951
**A classic newbie error: technically we can also add many `.then` to a single promise. This is not chaining.**
7052

7153
For example:
@@ -90,7 +72,7 @@ promise.then(function(result) {
9072
});
9173
```
9274

93-
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.
75+
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.
9476

9577
Here's the picture (compare it with the chaining above):
9678

@@ -102,9 +84,9 @@ In practice we rarely need multiple handlers for one promise. Chaining is used m
10284

10385
## Returning promises
10486

105-
Normally, a value returned by a `.then` handler is immediately passed to the next handler. But there's an exception.
87+
A handler, used in `.then(handler)` may create and return a promise.
10688

107-
If the returned value is a promise, then the further execution is suspended until it settles. After that, the result of that promise is given to the next `.then` handler.
89+
In that case further handlers wait until it settles, and then get its result.
10890

10991
For instance:
11092

@@ -138,15 +120,15 @@ new Promise(function(resolve, reject) {
138120
});
139121
```
140122

141-
Here the first `.then` shows `1` returns `new Promise(…)` in the line `(*)`. After one second it resolves, and the result (the argument of `resolve`, here it's `result*2`) is passed on to handler of the second `.then` in the line `(**)`. It shows `2` and does the same thing.
123+
Here the first `.then` shows `1` and returns `new Promise(…)` in the line `(*)`. After one second it resolves, and the result (the argument of `resolve`, here it's `result * 2`) is passed on to handler of the second `.then`. That handler is in the line `(**)`, it shows `2` and does the same thing.
142124

143-
So the output is again 1 -> 2 -> 4, but now with 1 second delay between `alert` calls.
125+
So the output is the same as in the previous example: 1 -> 2 -> 4, but now with 1 second delay between `alert` calls.
144126

145127
Returning promises allows us to build chains of asynchronous actions.
146128

147129
## Example: loadScript
148130

149-
Let's use this feature with `loadScript` to load scripts one by one, in sequence:
131+
Let's use this feature with the promisified `loadScript`, defined in the [previous chapter](info:promise-basics#loadscript), to load scripts one by one, in sequence:
150132

151133
```js run
152134
loadScript("/article/promise-chaining/one.js")
@@ -182,9 +164,9 @@ loadScript("/article/promise-chaining/one.js")
182164

183165
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.
184166

185-
We can add more asynchronous actions to the chain. Please note that code is still "flat", it grows down, not to the right. There are no signs of "pyramid of doom".
167+
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".
186168

187-
Please note that technically we can add `.then` directly to each `loadScript`, like this:
169+
Technically, we could add `.then` directly to each `loadScript`, like this:
188170

189171
```js run
190172
loadScript("/article/promise-chaining/one.js").then(script1 => {
@@ -207,11 +189,9 @@ Sometimes it's ok to write `.then` directly, because the nested function has acc
207189

208190

209191
````smart header="Thenables"
210-
To be precise, `.then` may return an arbitrary "thenable" object, and it will be treated the same way as a promise.
211-
212-
A "thenable" object is any object with a method `.then`.
192+
To be precise, a handler may return not exactly a promise, but a so-called "thenable" object - an arbitrary object that has a method `.then`. It will be treated the same way as a promise.
213193
214-
The idea is that 3rd-party libraries may implement "promise-compatible" objects of their own. They can have extended set of methods, but also be compatible with native promises, because they implement `.then`.
194+
The idea is that 3rd-party libraries may implement "promise-compatible" objects of their own. They can have an extended set of methods, but also be compatible with native promises, because they implement `.then`.
215195
216196
Here's an example of a thenable object:
217197
@@ -229,30 +209,32 @@ class Thenable {
229209
230210
new Promise(resolve => resolve(1))
231211
.then(result => {
212+
*!*
232213
return new Thenable(result); // (*)
214+
*/!*
233215
})
234216
.then(alert); // shows 2 after 1000ms
235217
```
236218
237-
JavaScript checks the object returned by `.then` handler in the line `(*)`: if it has a callable method named `then`, then it calls that method providing native functions `resolve`, `reject` as arguments (similar to executor) and waits until one of them is called. In the example above `resolve(2)` is called after 1 second `(**)`. Then the result is passed further down the chain.
219+
JavaScript checks the object returned by the `.then` handler in line `(*)`: if it has a callable method named `then`, then it calls that method providing native functions `resolve`, `reject` as arguments (similar to an executor) and waits until one of them is called. In the example above `resolve(2)` is called after 1 second `(**)`. Then the result is passed further down the chain.
238220
239-
This feature allows to integrate custom objects with promise chains without having to inherit from `Promise`.
221+
This feature allows us to integrate custom objects with promise chains without having to inherit from `Promise`.
240222
````
241223

242224

243225
## Bigger example: fetch
244226

245227
In frontend programming promises are often used for network requests. So let's see an extended example of that.
246228

247-
We'll use the [fetch](mdn:api/WindowOrWorkerGlobalScope/fetch) method to load the information about the user from the remote server. The method is quite complex, it has many optional parameters, but the basic usage is quite simple:
229+
We'll use the [fetch](info:fetch) method to load the information about the user from the remote server. It has a lot of optional parameters covered in [separate chapters](info:fetch), but the basic syntax is quite simple:
248230

249231
```js
250232
let promise = fetch(url);
251233
```
252234

253235
This makes a network request to the `url` and returns a promise. The promise resolves with a `response` object when the remote server responds with headers, but *before the full response is downloaded*.
254236

255-
To read the full response, we should call a method `response.text()`: it returns a promise that resolves when the full text downloaded from the remote server, with that text as a result.
237+
To read the full response, we should call the method `response.text()`: it returns a promise that resolves when the full text is downloaded from the remote server, with that text as a result.
256238

257239
The code below makes a request to `user.json` and loads its text from the server:
258240

@@ -261,7 +243,7 @@ fetch('/article/promise-chaining/user.json')
261243
// .then below runs when the remote server responds
262244
.then(function(response) {
263245
// response.text() returns a new promise that resolves with the full response text
264-
// when we finish downloading it
246+
// when it loads
265247
return response.text();
266248
})
267249
.then(function(text) {
@@ -270,27 +252,27 @@ fetch('/article/promise-chaining/user.json')
270252
});
271253
```
272254

273-
There is also a method `response.json()` that reads the remote data and parses it as JSON. In our case that's even more convenient, so let's switch to it.
255+
The `response` object returned from `fetch` also includes the method `response.json()` that reads the remote data and parses it as JSON. In our case that's even more convenient, so let's switch to it.
274256

275257
We'll also use arrow functions for brevity:
276258

277259
```js run
278260
// same as above, but response.json() parses the remote content as JSON
279261
fetch('/article/promise-chaining/user.json')
280262
.then(response => response.json())
281-
.then(user => alert(user.name)); // iliakan
263+
.then(user => alert(user.name)); // iliakan, got user name
282264
```
283265

284266
Now let's do something with the loaded user.
285267

286-
For instance, we can make one more request to github, load the user profile and show the avatar:
268+
For instance, we can make one more requests to GitHub, load the user profile and show the avatar:
287269

288270
```js run
289271
// Make a request for user.json
290272
fetch('/article/promise-chaining/user.json')
291273
// Load it as json
292274
.then(response => response.json())
293-
// Make a request to github
275+
// Make a request to GitHub
294276
.then(user => fetch(`https://api.github.com/users/${user.name}`))
295277
// Load the response as json
296278
.then(response => response.json())
@@ -305,7 +287,7 @@ fetch('/article/promise-chaining/user.json')
305287
});
306288
```
307289

308-
The code works, see comments about the details, but it should be quite self-descriptive. Although, there's a potential problem in it, a typical error of those who begin to use promises.
290+
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.
309291

310292
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.
311293

@@ -319,7 +301,7 @@ fetch('/article/promise-chaining/user.json')
319301
.then(user => fetch(`https://api.github.com/users/${user.name}`))
320302
.then(response => response.json())
321303
*!*
322-
.then(githubUser => new Promise(function(resolve, reject) {
304+
.then(githubUser => new Promise(function(resolve, reject) { // (*)
323305
*/!*
324306
let img = document.createElement('img');
325307
img.src = githubUser.avatar_url;
@@ -329,19 +311,17 @@ fetch('/article/promise-chaining/user.json')
329311
setTimeout(() => {
330312
img.remove();
331313
*!*
332-
resolve(githubUser);
314+
resolve(githubUser); // (**)
333315
*/!*
334316
}, 3000);
335317
}))
336318
// triggers after 3 seconds
337319
.then(githubUser => alert(`Finished showing ${githubUser.name}`));
338320
```
339321

340-
Now right after `setTimeout` runs `img.remove()`, it calls `resolve(githubUser)`, thus passing the control to the next `.then` in the chain and passing forward the user data.
341-
342-
As a rule, an asynchronous action should always return a promise.
322+
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.
343323

344-
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.
324+
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.
345325

346326
Finally, we can split the code into reusable functions:
347327

1-js/11-async/03-promise-chaining/head.html

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,6 @@
1010
document.head.append(script);
1111
});
1212
}
13-
14-
class HttpError extends Error {
15-
constructor(response) {
16-
super(`${response.status} for ${response.url}`);
17-
this.name = 'HttpError';
18-
this.response = response;
19-
}
20-
}
21-
22-
function loadJson(url) {
23-
return fetch(url)
24-
.then(response => {
25-
if (response.status == 200) {
26-
return response.json();
27-
} else {
28-
throw new HttpError(response);
29-
}
30-
})
31-
}
3213
</script>
3314

3415
<style>

0 commit comments

Comments
 (0)