Skip to content

Commit c4870fd

Browse files
committed
promise
1 parent 482ec27 commit c4870fd

File tree

1 file changed

+34
-28
lines changed

1 file changed

+34
-28
lines changed

6-async/02-promise-basics/article.md

Lines changed: 34 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ Everyone is happy: you, because the people don't crowd you any more, and fans, b
99
This is a real-life analogy for things we often have in programming:
1010

1111
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".
1313
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.
1414

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.
1616

1717
The constructor syntax for a promise object is:
1818

@@ -22,14 +22,14 @@ let promise = new Promise(function(resolve, reject) {
2222
});
2323
```
2424

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".
2626

2727
The resulting `promise` object has internal properties:
2828

2929
- `state` — initially "pending", then changes to either "fulfilled" or "rejected",
3030
- `result` — an arbitrary value of your choosing, initially `undefined`.
3131

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:
3333

3434
- `resolve(value)` — to indicate that the job finished successfully:
3535
- sets `state` to `"fulfilled"`,
@@ -40,15 +40,14 @@ When the executor finishes the job, it should call one of the following function
4040

4141
![](promise-resolve-reject.png)
4242

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`):
4446

4547
```js run
4648
let promise = new Promise(function(resolve, reject) {
4749
// the function is executed automatically when the promise is constructed
4850

49-
alert(resolve); // function () { [native code] }
50-
alert(reject); // function () { [native code] }
51-
5251
// after 1 second signal that the job is done with the result "done!"
5352
setTimeout(() => *!*resolve("done!")*/!*, 1000);
5453
});
@@ -100,26 +99,29 @@ Further, `resolve`/`reject` expect only one argument and will ignore additional
10099
````
101100

102101
```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.
104103
```
105104
106105
````smart header="Immediately calling `resolve`/`reject`"
107-
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:
108107
109108
```js
110109
let promise = new Promise(function(resolve, reject) {
110+
// not taking our time to do the job
111111
resolve(123); // immediately give the result: 123
112112
});
113113
```
114114

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.
116118
````
117119
118120
```smart header="The `state` and `result` are internal"
119121
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.
120122
```
121123
122-
## Consumers: `.then and `.catch`
124+
## Consumers: "then" and "catch"
123125
124126
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`.
125127
@@ -132,17 +134,17 @@ promise.then(
132134
);
133135
```
134136
135-
The first function argument:
137+
The first argument of `.then` is a function that:
136138
137139
1. runs when the Promise is resolved, and
138140
2. receives the result.
139141
140-
The second function argument:
142+
The second argument of `.then` is a function that:
141143
142144
1. runs when the Promise is rejected, and
143145
2. receives the error.
144146
145-
For instance:
147+
For instance, here's the reaction to a successfuly resolved promise:
146148
147149
```js run
148150
let promise = new Promise(function(resolve, reject) {
@@ -158,7 +160,9 @@ promise.then(
158160
);
159161
```
160162
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:
162166
163167
```js run
164168
let promise = new Promise(function(resolve, reject) {
@@ -186,7 +190,7 @@ promise.then(alert); // shows "done!" after 1 second
186190
*/!*
187191
```
188192
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:
190194
191195
192196
```js run
@@ -212,36 +216,38 @@ let promise = new Promise(resolve => resolve("done!"));
212216
promise.then(alert); // done! (shows up right now)
213217
```
214218
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.
216220
````
217221

218222
````smart header="Handlers of `.then`/`.catch` are always asynchronous"
219223
Even when the Promise is immediately resolved, code which occurs on lines *below* your `.then`/`.catch` may still execute first.
220224

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.
222226

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.
224228

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:
230232

231233
```js run
232234
// an "immediately" resolved Promise
233235
const executor = resolve => resolve("done!");
234236
const promise = new Promise(executor);
235237

236-
promise.then(alert); // this alert shows last
238+
promise.then(alert); // this alert shows last (*)
237239

238240
alert("code finished"); // this alert shows first
239241
```
240242

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.
242248
````
243249
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.
245251
246252
## Example: loadScript
247253

0 commit comments

Comments
 (0)