Skip to content

Commit 543680a

Browse files
committed
up
1 parent 0576ea7 commit 543680a

File tree

8 files changed

+114
-45
lines changed

8 files changed

+114
-45
lines changed

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

Lines changed: 114 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
# Promise
22

3-
A promise is an object of the built-in `Promise` class. It has the meaning of the "delayed result".
43

5-
The promise object has two inernal fields in it:
4+
```compare plus="Plus" minus="Minus"
5+
+ One
6+
- two
7+
```
68

7-
- `state` -- one of: "pending", "fulfilled", "rejected".
8-
- `result` -- when `new Promise` is created.
9+
A promise is an object of the built-in `Promise` class.
910

11+
The promise object has two main internal properties: `state` and the `result`.
1012

13+
The idea is that we have one place in the code that creates a promise object and manages its state and result ("producing" code), and other places that await for the result and then make use of it ("consuming" code).
1114

1215
The constructor syntax is:
1316

@@ -17,9 +20,18 @@ let promise = new Promise(function(resolve, reject) {
1720
});
1821
```
1922

20-
The function passed to `new Promise` is called *executor*. It is called automatically and immediately when the promise is created.
23+
The function passed to `new Promise` is called *executor*. When the promise is created, it is called automatically.
24+
25+
The purpose of that function is to do a job and then change the state of the promise by calling one of:
26+
27+
- `resolve(value)` -- to indicate that the job finished successfully: sets `state` to `"fulfilled"`, and `result` to `value`.
28+
- `reject(error)` -- to indicate that an error occured: sets `state` to `"rejected"` and `result` to `error`.
29+
30+
![](promise-resolve-reject.png)
2131

22-
So, if you run this, the output is shown immediately:
32+
Let's see examples.
33+
34+
First, a simplest executor, just to see how it works:
2335

2436
```js run
2537
let promise = new Promise(function(resolve, reject) {
@@ -28,14 +40,12 @@ let promise = new Promise(function(resolve, reject) {
2840
});
2941
```
3042

31-
The string description of `resolve` and `reject` may differ between engines, but they both are functions provided by JavaScript itself. We don't need to create them.
32-
33-
The executor should do a job, like loading a script, and at the end, it should call one of:
43+
We can see two things here:
3444

35-
- `resolve(result)` -- to indicate that the job finished successfully with the `result`.
36-
- `reject(error)` -- to indicate that an error occured, and `error` should be the `Error` object (technically can be any value).
45+
1. The executor is called immediately by `new Promise`.
46+
2. Both functions `resolve` and `reject` are provided by JavaScript itself. We don't need to create them. Their string representation may vary between JavaScript engines.
3747

38-
Like this:
48+
Now let's wait 1 second and then resolve the promise:
3949

4050
```js
4151
let promise = new Promise(function(*!*resolve*/!*, reject) {
@@ -46,7 +56,9 @@ let promise = new Promise(function(*!*resolve*/!*, reject) {
4656
});
4757
```
4858

49-
Or, in case of an error:
59+
![](promise-resolve-1.png)
60+
61+
Here we wait 1 second and then reject the promise with an error:
5062

5163
```js
5264
let promise = new Promise(function(resolve, *!*reject*/!*) {
@@ -55,71 +67,121 @@ let promise = new Promise(function(resolve, *!*reject*/!*) {
5567
});
5668
```
5769

58-
## Promise state
70+
![](promise-reject-1.png)
71+
72+
The promise that was either resolved or rejected is called "settled".
73+
74+
We must call only one `resolve` or `reject`. And that's final.
75+
76+
All calls of `resolve` and `reject` after the first one are ignored:
77+
78+
```js
79+
let promise = new Promise(function(resolve, reject) {
80+
resolve("done");
81+
82+
reject(new Error("")); // ignored
83+
setTimeout(() => resolve("")); // ignored
84+
});
85+
```
86+
87+
The idea is that a job may have only one result or an error.
5988

60-
The promise object has an internal state. The `Promise` constructor sets it to "pending". Then it may change in two ways.
89+
There are other data structures for jobs that have many results, for instance streams and queues. But they are not supproted by JavaScript language core and lack certain features that promises provide.
6190

62-
When `resolve` is called, the state becomes "fulfilled". When `reject` is called, the state becomes "rejected":
91+
```smart header="Reject with `Error` objects"
92+
Technically we can call `resolve` and `reject` with any value. But it's recommended to use `Error` objects in `reject` (or inherited from them).
93+
```
6394
64-
![](promiseInit.png)
95+
````smart header="Resolve/reject can be immediate"
96+
In practice an executor usually does something asynchronously, but it doesn't have to. We can call `resolve` or `reject` immediately, like this:
6597
66-
```smart
67-
The promise that is either resolved or rejected is also called "settled" (as opposed to "pending").
98+
```js
99+
let promise = new Promise(function(resolve, reject) {
100+
resolve(123);
101+
});
68102
```
69103

70-
The external code may add "handlers" to run when the promise becomes settled.
104+
````
105+
106+
107+
## Consumers: ".then" and ".catch"
108+
109+
A promise object allows to add "handlers" to run when it becomes settled.
71110
72-
There are two methods for that:
111+
The syntax is:
112+
113+
```js
114+
promise.then(
115+
function(result) { /* process a sucessful result */ },
116+
function(error) { /* process an error */ }
117+
);
118+
```
73119
74-
- `promise.then(onResolve)` schedules the function `onResolve` to call when the promise is fulfilled, and it gets the result.
75-
- `promise.catch(onReject)` schedules the function `onReject` to call when the promise is rejected, and it gets the error.
120+
The first argument of `then` runs on `resolve` and gets the result, and the second one -- on `reject` and gets the error.
76121
77-
For instance, here's how to react on the successful job completion:
122+
For example:
78123
79124
```js run
80125
let promise = new Promise(function(resolve, reject) {
81126
setTimeout(() => resolve("done!"), 1000);
82127
});
83128
84-
// shows "done!" after 1 second
85-
promise.then(result => alert(result));
129+
// resolve runs the first function in .then
130+
promise.then(
131+
*!*
132+
result => alert(result), // shows "done!" after 1 second
133+
*/!*
134+
error => alert(error) // doesn't run
135+
);
86136
```
87137
88-
...And here's the reacton on an error:
89-
90138
```js run
91139
let promise = new Promise(function(resolve, reject) {
92140
setTimeout(() => reject(new Error("Whoops!")), 1000);
93141
});
94142
95-
// shows "Whoops!" after 1 second
96-
promise.catch(error => alert(error.message));
143+
// reject runs the second function in .then
144+
promise.then(
145+
result => alert(result), // doesn't run
146+
*!*
147+
error => alert(error) // shows "Error: Whoops!" after 1 second
148+
*/!*
149+
);
97150
```
98151
99-
100-
Also we can call `promise.then(onResolve, onReject)` to set both handlers at once. Technically, `promise.catch(func)` works the same way as `promise.then(null, func)`.
152+
If we're interested only in successful completions, then we can provide only one argument to `.then`:
101153
102154
```js run
103-
let promise = new Promise(function(resolve, reject) {
155+
let promise = new Promise(resolve => {
104156
setTimeout(() => resolve("done!"), 1000);
105157
});
106158
107159
*!*
108-
// set both success and error handlers
109-
promise.then(
110-
result => alert(result),
111-
error => alert(error)
112-
);
160+
promise.then(alert); // shows "done!" after 1 second
113161
*/!*
114162
```
115163
116-
So, when we want to do something asynchronously, we can create a promise with the proper executor to do a job, and then add handlers to it. They run when it finishes.
164+
If we're interested only in errors, then we can use `.then(null, function)` or a special shorthand: `.catch`
165+
166+
167+
```js run
168+
let promise = new Promise((resolve, reject) => {
169+
setTimeout(() => reject(new Error("Whoops!")), 1000);
170+
});
171+
172+
*!*
173+
// same as promise.then(null, alert)
174+
promise.catch(alert); // shows "Error: Whoops!" after 1 second
175+
*/!*
176+
```
117177
118-
Let's see more examples to explore the benefits over the callback-based approach.
178+
Now let's see more practical examples to explore the benefits over the callback-based approach.
119179
120180
## Example: loadScript
121181
122-
We have the `loadScript` function for loading a script from the previous chapter, here's the callback-based variant:
182+
We have the `loadScript` function for loading a script from the previous chapter.
183+
184+
Here's the callback-based variant:
123185
124186
```js
125187
function loadScript(src, callback) {
@@ -135,7 +197,7 @@ function loadScript(src, callback) {
135197
136198
Let's rewrite it using promises.
137199
138-
The call to `loadScript(src)` below returns a promise that resolves/rejects when the loading is complete:
200+
The new function `loadScript` will not require a callback. Instead it will create and return a promise object that settles when the loading is complete. The outer code can add handlers to it using `.then`:
139201
140202
```js run
141203
function loadScript(src) {
@@ -159,13 +221,20 @@ promise.then(
159221
error => alert(`Error: ${error.message}`);
160222
);
161223
162-
promise.then(script => alert('One more handler to do something else with the script'));
224+
promise.then(script => alert('One more handler to do something else!'));
163225
```
164226
165-
We can immediately see some benefits over the callback-based syntax in the example above:
227+
We can immediately see few benefits over the callback-based syntax in the example above.
228+
229+
| Callbacks | Promises |
230+
|-----------|----------|
231+
| We must have `callback` function when calling `loadScript`. So we must know what to do with the result *before* we make a call for it. | Promises allow us to code more naturally. First we run `loadScript`, then code what we do with the result. |
232+
|There can be only one callback. | We can call `.then` as many times as we want. |
233+
234+
235+
We can call `promise.then` at any time. Maybe much later, when we really need that script.
166236
167237
1. We can call `promise.then` as many times as want, so we can add any number of handlers.
168-
2. We can call `promise.then` at any time. Maybe much later, when we really need that script.
169238
3. The `promise` object can be passed around, new handlers can be added where needed in other parts of the code.
170239
171240
So promises give us flexibility. But there's more. We can chain promises and use `async` functions and so on. We'll see that in the next chapters.
13.4 KB
Loading
30 KB
Loading
14.3 KB
Loading
32.1 KB
Loading
9.77 KB
Loading
22.7 KB
Loading

figures.sketch

-9.62 MB
Binary file not shown.

0 commit comments

Comments
 (0)