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: 6-async/02-promise-basics/article.md
+34-28Lines changed: 34 additions & 28 deletions
Original file line number
Diff line number
Diff line change
@@ -9,10 +9,10 @@ Everyone is happy: you, because the people don't crowd you any more, and fans, b
9
9
This is a real-life analogy for things we often have in programming:
10
10
11
11
1. A "producing code" that does something and takes time. For instance, the code loads a remote script. That's a "singer".
12
-
2. A "consuming code" that wants the result of the "producing code" once it's ready. Many functions (in your "consuming code") may need that result. These are the "fans".
12
+
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".
13
13
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.
14
14
15
-
The analogy isn't terribly accurate, because JavaScript promises are more complex than a simple subscription list: they have additional features and limitations. But this will serve for an introduction.
15
+
The analogy isn't terribly accurate, because JavaScript promises are more complex than a simple subscription list: they have additional features and limitations. But it's fine to begin with.
16
16
17
17
The constructor syntax for a promise object is:
18
18
@@ -22,14 +22,14 @@ let promise = new Promise(function(resolve, reject) {
22
22
});
23
23
```
24
24
25
-
The function passed to `new Promise` is called the *executor*. When the promise is created, this executor function is called (or, run) automatically. It contains the producing code, that should eventually produce a result. In terms of the analogy above: the executor is the "singer".
25
+
The function passed to `new Promise` is called the *executor*. When the promise is created, this executor function runs automatically. It contains the producing code, that should eventually produce a result. In terms of the analogy above: the executor is the "singer".
26
26
27
27
The resulting `promise` object has internal properties:
28
28
29
29
-`state` — initially "pending", then changes to either "fulfilled" or "rejected",
30
30
-`result` — an arbitrary value of your choosing, initially `undefined`.
31
31
32
-
When the executor finishes the job, it should call one of the following functions:
32
+
When the executor finishes the job, it should call one of the functions that it gets as arguments:
33
33
34
34
-`resolve(value)` — to indicate that the job finished successfully:
35
35
- sets `state` to `"fulfilled"`,
@@ -40,15 +40,14 @@ When the executor finishes the job, it should call one of the following function
40
40
41
41

42
42
43
-
To gather all of that together: here's an example of a Promise constructor and a simple executor function with its "producing code" (the `setTimeout`):
43
+
Later we'll see how these changes become known to "fans".
44
+
45
+
Here's an example of a Promise constructor and a simple executor function with its "producing code" (the `setTimeout`):
44
46
45
47
```js run
46
48
let promise =newPromise(function(resolve, reject) {
47
49
// the function is executed automatically when the promise is constructed
48
50
49
-
alert(resolve); // function () { [native code] }
50
-
alert(reject); // function () { [native code] }
51
-
52
51
// after 1 second signal that the job is done with the result "done!"
53
52
setTimeout(() =>*!*resolve("done!")*/!*, 1000);
54
53
});
@@ -100,26 +99,29 @@ Further, `resolve`/`reject` expect only one argument and will ignore additional
100
99
````
101
100
102
101
```smart header="Reject with `Error` objects"
103
-
Technically, we can pass any type of argument to `reject` (just like `resolve`). But it is recommended to use `Error` objects (or objects that inherit from `Error`). The reasoning for that will soon become apparent.
102
+
In case if something goes wrong, we can call `reject` with any type of argument (just like `resolve`). But it is recommended to use `Error` objects (or objects that inherit from `Error`). The reasoning for that will soon become apparent.
In practice, an executor usually does something asynchronously and calls `resolve`/`reject` after some time, but it doesn't have to. We can call `resolve` or `reject` immediately, like this:
106
+
In practice, an executor usually does something asynchronously and calls `resolve`/`reject` after some time, but it doesn't have to. We also can call `resolve` or `reject` immediately, like this:
108
107
109
108
```js
110
109
let promise = new Promise(function(resolve, reject) {
110
+
// not taking our time to do the job
111
111
resolve(123); // immediately give the result: 123
112
112
});
113
113
```
114
114
115
-
For instance, this might happen when we start to do a job but then see that everything has already been completed. That is fine: we immediately have a resolved Promise.
115
+
For instance, this might happen when we start to do a job but then see that everything has already been completed.
116
+
117
+
That's fine. We immediately have a resolved Promise, nothing wrong with that.
116
118
````
117
119
118
120
```smart header="The `state` and `result` are internal"
119
121
The properties `state` and `result` of the Promise object are internal. We can't directly access them from our "consuming code". We can use the methods `.then`/`.catch` for that. They are described below.
120
122
```
121
123
122
-
## Consumers: `.then and `.catch`
124
+
## Consumers: "then" and "catch"
123
125
124
126
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 the methods `.then` and `.catch`.
125
127
@@ -132,17 +134,17 @@ promise.then(
132
134
);
133
135
```
134
136
135
-
The first function argument:
137
+
The first argument of `.then` is a function that:
136
138
137
139
1. runs when the Promise is resolved, and
138
140
2. receives the result.
139
141
140
-
The second function argument:
142
+
The second argument of `.then` is a function that:
141
143
142
144
1. runs when the Promise is rejected, and
143
145
2. receives the error.
144
146
145
-
For instance:
147
+
For instance, here's the reaction to a successfuly resolved promise:
146
148
147
149
```js run
148
150
let promise = new Promise(function(resolve, reject) {
@@ -158,7 +160,9 @@ promise.then(
158
160
);
159
161
```
160
162
161
-
In the case of a rejection:
163
+
The first function was executed.
164
+
165
+
And in the case of a rejection -- the second one:
162
166
163
167
```js run
164
168
let promise = new Promise(function(resolve, reject) {
@@ -186,7 +190,7 @@ promise.then(alert); // shows "done!" after 1 second
186
190
*/!*
187
191
```
188
192
189
-
If we're interested only in errors, then we can use `.catch(errorHandlingFunction)`, which is exactly the same as `.then(null, errorHandlingFunction)`.
193
+
If we're interested only in errors, then we can use `null` as the first argument: `.then(null, errorHandlingFunction)`. Or we can use `.catch(errorHandlingFunction)`, which is exactly the same:
190
194
191
195
192
196
```js run
@@ -212,36 +216,38 @@ let promise = new Promise(resolve => resolve("done!"));
212
216
promise.then(alert); // done! (shows up right now)
213
217
```
214
218
215
-
That's handy for jobs that may sometimes require time and sometimes finish immediately. The handler is guaranteed to run in both cases.
219
+
Some tasks may sometimes require time and sometimes finish immediately. The good thing is: the `.then` handler is guaranteed to run in both cases.
216
220
````
217
221
218
222
````smart header="Handlers of `.then`/`.catch` are always asynchronous"
219
223
Even when the Promise is immediately resolved, code which occurs on lines *below* your `.then`/`.catch` may still execute first.
220
224
221
-
When it is time for the function you've passed to `.then`/`.catch`to execute, the JavaScript engine puts it at the end of an internal execution queue.
225
+
The JavaScript engine has an internal execution queue which gets all `.then/catch`handlers.
222
226
223
-
The JavaScript engine doesn't wait very long for an operation to finish before moving on to do other things. Everything is racing to get done. So as you gain experience, you will encounter situations where some lines of code are still "in process" while *later* lines have already started. In the example code below, you can imagine that the events might occur in this order, behind the scenes:
227
+
But it only looks into that queue when the current execution is finished.
224
228
225
-
1. The new Promise object is constructed at Line 3, and the executor is passed on to it.
226
-
2. The subscriber at Line 5 (the call to `alert`) is registered with the Promise.
227
-
3. The executor is finally run by the Promise object and immediately resolves.
228
-
4. The second `alert` call, at Line 7, is executed.
229
-
6. The Promise notices that it has been resolved and therefore executes the registered call to `alert` from Line 5.
229
+
In other words, `.then/catch` handlers are pending execution until the engine is done with the current code.
230
+
231
+
For instance, here:
230
232
231
233
```js run
232
234
// an "immediately" resolved Promise
233
235
constexecutor=resolve=>resolve("done!");
234
236
constpromise=newPromise(executor);
235
237
236
-
promise.then(alert); // this alert shows last
238
+
promise.then(alert); // this alert shows last (*)
237
239
238
240
alert("code finished"); // this alert shows first
239
241
```
240
242
241
-
So the code *after*`.then` ends up always running *before* the Promise's subscribers, even in the case of an immediately-resolved Promise. Usually that's unimportant, in some scenarios it may matter a great deal.
243
+
The promise becomes settled immediately, but the engine first finishes the current code, calls `alert`, and only *afterwards* looks into the queue to run `.then` handler.
244
+
245
+
So the code *after*`.then` ends up always running *before* the Promise's subscribers, even in the case of an immediately-resolved Promise.
246
+
247
+
Usually that's unimportant, but in some scenarios the order may matter a great deal.
242
248
````
243
249
244
-
Now, let's see more practical examples of how promises can help us to write asynchronous code.
250
+
Next, let's see more practical examples of how promises can help us to write asynchronous code.
0 commit comments