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/04-object-basics/03-symbol/article.md
+11-2Lines changed: 11 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -18,7 +18,7 @@ let id = Symbol();
18
18
19
19
We can also give symbol a description (also called a symbol name), mostly useful for debugging purposes:
20
20
21
-
```js
21
+
```js run
22
22
// id is a symbol with the description "id"
23
23
let id =Symbol("id");
24
24
```
@@ -50,6 +50,8 @@ alert(id); // TypeError: Cannot convert a Symbol value to a string
50
50
*/!*
51
51
```
52
52
53
+
That's a "language guard" against messing up, because strings and symbols are fundamentally different and should not occasionally convert one into another.
54
+
53
55
If we really want to show a symbol, we need to call `.toString()` on it, like here:
54
56
```js run
55
57
let id = Symbol("id");
@@ -58,7 +60,14 @@ alert(id.toString()); // Symbol(id), now it works
58
60
*/!*
59
61
```
60
62
61
-
That's a "language guard" against messing up, because strings and symbols are fundamentally different and should not occasionally convert one into another.
63
+
Or get `symbol.description` property to get the description only:
Copy file name to clipboardExpand all lines: 1-js/11-async/02-promise-basics/article.md
+12-34Lines changed: 12 additions & 34 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -214,9 +214,13 @@ The call `.catch(f)` is a complete analog of `.then(null, f)`, it's just a short
214
214
215
215
### finally
216
216
217
-
The call `.finally(f)` is similar to `.then(f, f)`, it always runs when the promise is settled: be it resolve or reject.
217
+
Just like there's a finally clause in a regular `try {...} catch {...}`, there's `finally` in promises.
218
218
219
-
The idea is that we can perform cleanup in it, e.g. stop our loading indicators in `finally`, as they are not needed any more, like this:
219
+
The call `.finally(f)` is similar to `.then(f, f)` in the sense that it always runs when the promise is settled: be it resolve or reject.
220
+
221
+
It is a good handler to perform cleanup, e.g. to stop our loading indicators in `finally`, as they are not needed any more, no matter what the outcome is.
222
+
223
+
Like this:
220
224
221
225
```js
222
226
new Promise((resolve, reject) => {
@@ -231,7 +235,7 @@ new Promise((resolve, reject) => {
231
235
232
236
It's not exactly an alias though. There are several important differences:
233
237
234
-
1. A `finally` handler has no arguments. In `finally` we don't know whether the promise is successful or not. We shouldn't need to know it, as our task is usually to perform "general" finalizing procedures.
238
+
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.
235
239
2. Finally passes through results and errors to the next handler.
236
240
237
241
For instance, here the result is passed through `finally` to `then`:
@@ -240,7 +244,7 @@ It's not exactly an alias though. There are several important differences:
240
244
setTimeout(() => resolve("result"), 2000)
241
245
})
242
246
.finally(() => alert("Promise ready"))
243
-
.then(result => alert(result)); // result
247
+
.then(result => alert(result)); // <-- .then handles the result
244
248
```
245
249
246
250
And here there's an error in the promise, passed through `finally` to `catch`:
@@ -250,14 +254,14 @@ It's not exactly an alias though. There are several important differences:
250
254
throw new Error("error");
251
255
})
252
256
.finally(() => alert("Promise ready"))
253
-
.catch(err => alert(err)); // error
257
+
.catch(err => alert(err)); // <-- .catch handles the error object
254
258
```
255
259
256
260
That's very convenient, because finally is not meant to process promise results. So it passes them through.
257
261
258
-
We'll talk about promise chaining and passing around results in more detail in the next chapter.
262
+
We'll talk more about promise chaining and result-passing between handlers in the next chapter.
259
263
260
-
3. The last, but not the least, `.finally(f)` is more convenient syntax than `.then(f, f)`: there's no need to duplicate a function.
264
+
3. The last, but not the least, `.finally(f)` is a more convenient syntax than `.then(f, f)`: no need to duplicate the function.
If a promise is pending, `.then/catch/finally` handlers wait for the result. Otherwise, if a promise has already settled, they execute immediately:
@@ -269,33 +273,7 @@ let promise = new Promise(resolve => resolve("done!"));
269
273
promise.then(alert); // done! (shows up right now)
270
274
```
271
275
272
-
Some tasks may sometimes require time and sometimes finish immediately. The good thing is: the `.then` handler is guaranteed to run in both cases.
273
-
````
274
-
275
-
````smart header="Handlers of `.then`/`.catch`/`.finally` are always asynchronous"
276
-
Even when the Promise is immediately resolved, code which occurs on lines *below* your `.then`/`.catch`/`.finally` may still execute first.
277
-
278
-
The JavaScript engine has an internal execution queue which gets all `.then/catch/finally` handlers.
279
-
280
-
But it only looks into that queue when the current execution is finished. In other words, the handlers are pending execution until the engine is done with the current code.
281
-
282
-
For instance, here:
283
-
284
-
```js run
285
-
// an "immediately" resolved Promise
286
-
constexecutor=resolve=>resolve("done!");
287
-
constpromise=newPromise(executor);
288
-
289
-
promise.then(alert); // this alert shows last (*)
290
-
291
-
alert("code finished"); // this alert shows first
292
-
```
293
-
294
-
The promise becomes settled immediately, but the engine first finishes the current code, calls `alert("code finished")`, and only *afterwards* looks into the queue to run `.then` handler.
295
-
296
-
So the code *after*`.then` ends up always running *before* the Promise's subscribers, even in the case of an immediately-resolved Promise.
297
-
298
-
Sometimes that's unimportant, while in some scenarios the order may matter a great deal.
276
+
The good thing is: `.then` handler is guaranteed to run whether the promise takes time or settles it immediately.
299
277
````
300
278
301
279
Next, let's see more practical examples of how promises can help us to write asynchronous code.
Promise handlers `.then`/`.catch`/`.finally` are always asynchronous.
5
+
6
+
Even when a Promise is immediately resolved, the code on the lines *below* your `.then`/`.catch`/`.finally` may still execute first.
7
+
8
+
Here's the code that demonstrates it:
9
+
10
+
```js run
11
+
// an "immediately" resolved Promise
12
+
newPromise(resolve=>resolve("promise done!"))
13
+
.then(alert); // this alert shows after the alert below
14
+
15
+
alert("code finished"); // this alert shows first
16
+
```
17
+
18
+
If you run it, you see `code finished` first, and then `promise done!`.
19
+
20
+
What's going on?
21
+
22
+
# Internal queue
23
+
24
+
Asynchronous tasks need proper management. For that, the standard specifies an internal queue of "Promise Jobs".
25
+
26
+
As said in the [specification](https://tc39.github.io/ecma262/#sec-jobs-and-job-queues):
27
+
28
+
- The queue is first-in-first-out: jobs that get enqueued first are run first.
29
+
- Execution of a job is initiated only when there is no running execution context, and the execution context stack is empty.
30
+
31
+
When a promise is ready, its `.then/catch/finally` handler is not executed right ahead. Instead, the handling is put into the queue.
32
+
33
+
Javascript engine takes a job from the queue and executes it, when it finishes executing the current code.
34
+
35
+
That's why "code finished" in the example above shows first.
36
+
37
+

38
+
39
+
If there's a chain with multiple `.then/catch/finally`, then every one of them is executed asynchronously. In other words, it first gets queued, and then executed, when all handlers before it are finished.
40
+
41
+
What if the order matters for us, and we want to see `code finished` after `promise done`?
42
+
43
+
Easy, just put it into the queue:
44
+
45
+
```js run
46
+
newPromise(resolve=>resolve("promise done!"))
47
+
.then(alert)
48
+
.then(() =>alert("code finished");
49
+
```
50
+
51
+
Now the order is as intended.
52
+
53
+
## Higher-order queues
54
+
55
+
There are other action queues, depending on the environment.
56
+
57
+
For instance, `setTimeout` enqueues an action when the time comes, or even right now if the timeout is zero:
58
+
59
+
```js
60
+
setTimeout(handler, 0); // handler is queued for immediate execution.
61
+
```
62
+
63
+
Other examples:
64
+
- Pending events (like mouse movements in the browser)
65
+
- Network operations that may take time, or may finish immediately if the result is cached.
66
+
67
+
**Promise queue has higher priority than environment-related queues.**
68
+
69
+
For instance, take a look:
70
+
71
+
```js run
72
+
setTimeout(() =>alert("timeout"), 0);
73
+
74
+
newPromise(resolve=>resolve("promise"))
75
+
.then(alert);
76
+
77
+
alert("code");
78
+
```
79
+
80
+
1. `code` shows first, it's a regular synchronous call.
81
+
2. `promise` shows second, because `.then` passes through the promise queue, runs after the current code.
82
+
3. `timeout` shows last, because environment-specific queue has lower priority.
83
+
84
+
That's also true for more complex calls, e.g if we schedule an immediate `setTimeout` call inside the promise, then it also executes last:
85
+
86
+
```js run
87
+
newPromise(resolve=> {
88
+
setTimeout(() =>alert("timeout"), 0);
89
+
resolve("promise"); // shows up first
90
+
}).then(alert);
91
+
```
92
+
93
+
Here also `promise` triggers first, because promise actions have higher priority.
94
+
95
+
## Summary
96
+
97
+
Promise handling is always async, as all promise actions pass through the internal "promise jobs" queue.
98
+
99
+
**So, `.then/catch/finally` is called after the current code is finished.**
100
+
101
+
If we need to guarantee that a piece of code is executed after `.then/catch/finally`, it's best to add it into a chained `.then` call.
102
+
103
+
Other environments may have their own async actions, like events, network-related calls, filesystem tasks, and `setTimeout`-scheduled calls.
104
+
105
+
**Environment-specific async actions happen after the code is finished *and* after the promise queue is empty.**
106
+
107
+
In other words, they have lower priority.
108
+
109
+
So we know for sure a promise chain goes as far as possible first. It may finish or hang waiting for something outside of the promise queue, and only then an event-related handler or `setTimeout` may trigger.
0 commit comments