You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: 1-js/11-async/02-promise-basics/article.md
+44-21
Original file line number
Diff line number
Diff line change
@@ -127,9 +127,9 @@ That's fine. We immediately have a resolved promise.
127
127
The properties `state` and `result` of the Promise object are internal. We can't directly access them. We can use the methods `.then`/`.catch`/`.finally` for that. They are described below.
128
128
```
129
129
130
-
## Consumers: then, catch, finally
130
+
## Consumers: then, catch
131
131
132
-
A Promise object serves as a link between the executor (the "producing code" or "singer") and the consuming functions (the "fans"), which will receive the result or error. Consuming functions can be registered (subscribed) using methods `.then`, `.catch` and `.finally`.
132
+
A Promise object serves as a link between the executor (the "producing code" or "singer") and the consuming functions (the "fans"), which will receive the result or error. Consuming functions can be registered (subscribed) using methods `.then`and `.catch`.
133
133
134
134
### then
135
135
@@ -212,59 +212,82 @@ promise.catch(alert); // shows "Error: Whoops!" after 1 second
212
212
213
213
The call `.catch(f)` is a complete analog of `.then(null, f)`, it's just a shorthand.
214
214
215
-
### finally
215
+
## Cleanup: finally
216
216
217
217
Just like there's a `finally` clause in a regular `try {...} catch {...}`, there's `finally` in promises.
218
218
219
-
The call `.finally(f)` is similar to `.then(f, f)` in the sense that `f` always runs when the promise is settled: be it resolve or reject.
219
+
The call `.finally(f)` is similar to `.then(f, f)` in the sense that `f` runs always, when the promise is settled: be it resolve or reject.
220
220
221
-
`finally` is a good handler for performing cleanup, e.g. stopping our loading indicators, as they are not needed anymore, no matter what the outcome is.
221
+
The idea of `finally` is to set up a handler for performing cleanup/finalizing after the previous operations are complete.
222
222
223
-
Like this:
223
+
E.g. stopping loading indicators, closing no longer needed connections etc.
224
+
225
+
Think of it as a party finisher. No matter was a party good or bad, how many friends were in it, we still need (or at least should) do a cleanup after it.
226
+
227
+
The code may look like this:
224
228
225
229
```js
226
230
new Promise((resolve, reject) => {
227
-
/* do something that takes time, and then call resolve/reject */
231
+
/* do something that takes time, and then call resolve or maybe reject */
228
232
})
229
233
*!*
230
234
// runs when the promise is settled, doesn't matter successfully or not
231
235
.finally(() => stop loading indicator)
232
-
// so the loading indicator is always stopped before we process the result/error
236
+
// so the loading indicator is always stopped before we go on
233
237
*/!*
234
238
.then(result => show result, err => show error)
235
239
```
236
240
237
-
That said, `finally(f)` isn't exactly an alias of `then(f,f)` though. There are few subtle differences:
241
+
Please note that `finally(f)` isn't exactly an alias of `then(f,f)` though.
242
+
243
+
There are important differences:
238
244
239
245
1. A `finally` handler has no arguments. In `finally` we don't know whether the promise is successful or not. That's all right, as our task is usually to perform "general" finalizing procedures.
240
-
2. A `finally` handler "passes through" the result or error to the next handler.
246
+
247
+
Please take a look at the example above: as you can see, the `finally` handler has no arguments, and the promise outcome is handled in the next handler.
248
+
2. A `finally` handler "passes through" the result or error to the next suitable handler.
241
249
242
250
For instance, here the result is passed through `finally` to `then`:
251
+
243
252
```js run
244
253
new Promise((resolve, reject) => {
245
-
setTimeout(() => resolve("result"), 2000)
254
+
setTimeout(() => resolve("value"), 2000);
246
255
})
247
-
.finally(() => alert("Promise ready"))
248
-
.then(result => alert(result)); // <-- .then handles the result
256
+
.finally(() => alert("Promise ready")) // triggers first
And here there's an error in the promise, passed through `finally` to `catch`:
260
+
As you can see, the `value` returned by the first promise is passed through `finally` to the next `then`.
261
+
262
+
That's very convenient, because `finally` is not meant to process a promise result. As said, it's a place to do generic cleanup, no matter what the outcome was.
263
+
264
+
And here's an example of an error, for us to see how it's passed through `finally` to `catch`:
252
265
253
266
```js run
254
267
new Promise((resolve, reject) => {
255
268
throw new Error("error");
256
269
})
257
-
.finally(() => alert("Promise ready"))
258
-
.catch(err => alert(err)); // <-- .catch handles the error object
270
+
.finally(() => alert("Promise ready")) // triggers first
271
+
.catch(err => alert(err)); // <-- .catch shows the error
259
272
```
260
273
261
-
That's very convenient, because `finally` is not meant to process a promise result. So it passes it through.
274
+
3. A `finally` handler also shouldn't return anything. If it does, the returned value is silently ignored.
275
+
276
+
The only exception from this rule is when a `finally` handler throws an error. Then this error goes to the next handler, instead of any previous outcome.
262
277
263
-
We'll talk more about promise chaining and result-passing between handlers in the next chapter.
278
+
To summarize:
264
279
280
+
- A `finally` handler doesn't get the outcome of the previous handler (it has no arguments). This outcome is passed through instead, to the next suitable handler.
281
+
- If a `finally` handler returns something, it's ignored. The notable exception is when `finally` throws an error, then the execution goes to a nearest error handler.
282
+
283
+
That's all fine if we use `finally` the right way, how it's supposed to be used: for generic cleanup procedures.
265
284
266
285
````smart header="We can attach handlers to settled promises"
267
-
If a promise is pending, `.then/catch/finally` handlers wait for it. Otherwise, if a promise has already settled, they just run:
286
+
If a promise is pending, `.then/catch/finally` handlers wait for its outcome.
287
+
288
+
Sometimes, it might be that a promise is already settled when we add a handler to it.
289
+
290
+
In such case, these handlers just run immediately:
268
291
269
292
```js run
270
293
// the promise becomes resolved immediately upon creation
@@ -278,10 +301,10 @@ Note that this makes promises more powerful than the real life "subscription lis
278
301
Promises are more flexible. We can add handlers any time: if the result is already there, they just execute.
279
302
````
280
303
281
-
Next, let's see more practical examples of how promises can help us write asynchronous code.
282
-
283
304
## Example: loadScript [#loadscript]
284
305
306
+
Next, let's see more practical examples of how promises can help us write asynchronous code.
307
+
285
308
We've got the `loadScript` function for loading a script from the previous chapter.
286
309
287
310
Here's the callback-based variant, just to remind us of it:
0 commit comments