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
A promise is an object of the built-in `Promise` class. It has the meaning of the "delayed result".
4
3
5
-
The promise object has two inernal fields in it:
4
+
```compare plus="Plus" minus="Minus"
5
+
+ One
6
+
- two
7
+
```
6
8
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.
9
10
11
+
The promise object has two main internal properties: `state` and the `result`.
10
12
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).
11
14
12
15
The constructor syntax is:
13
16
@@ -17,9 +20,18 @@ let promise = new Promise(function(resolve, reject) {
17
20
});
18
21
```
19
22
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
+

21
31
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:
23
35
24
36
```js run
25
37
let promise =newPromise(function(resolve, reject) {
@@ -28,14 +40,12 @@ let promise = new Promise(function(resolve, reject) {
28
40
});
29
41
```
30
42
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:
34
44
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.
37
47
38
-
Like this:
48
+
Now let's wait 1 second and then resolve the promise:
39
49
40
50
```js
41
51
let promise =newPromise(function(*!*resolve*/!*, reject) {
@@ -46,7 +56,9 @@ let promise = new Promise(function(*!*resolve*/!*, reject) {
46
56
});
47
57
```
48
58
49
-
Or, in case of an error:
59
+

60
+
61
+
Here we wait 1 second and then reject the promise with an error:
50
62
51
63
```js
52
64
let promise =newPromise(function(resolve, *!*reject*/!*) {
@@ -55,71 +67,121 @@ let promise = new Promise(function(resolve, *!*reject*/!*) {
55
67
});
56
68
```
57
69
58
-
## Promise state
70
+

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 =newPromise(function(resolve, reject) {
80
+
resolve("done");
81
+
82
+
reject(newError("…")); // ignored
83
+
setTimeout(() =>resolve("…")); // ignored
84
+
});
85
+
```
86
+
87
+
The idea is that a job may have only one result or an error.
59
88
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.
61
90
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
+
```
63
94
64
-

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:
65
97
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
+
});
68
102
```
69
103
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.
71
110
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
+
```
73
119
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.
76
121
77
-
For instance, here's how to react on the successful job completion:
122
+
For example:
78
123
79
124
```js run
80
125
let promise = new Promise(function(resolve, reject) {
81
126
setTimeout(() => resolve("done!"), 1000);
82
127
});
83
128
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
+
);
86
136
```
87
137
88
-
...And here's the reacton on an error:
89
-
90
138
```js run
91
139
let promise = new Promise(function(resolve, reject) {
error => alert(error) // shows "Error: Whoops!" after 1 second
148
+
*/!*
149
+
);
97
150
```
98
151
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`:
101
153
102
154
```js run
103
-
let promise =newPromise(function(resolve, reject) {
155
+
let promise = new Promise(resolve => {
104
156
setTimeout(() => resolve("done!"), 1000);
105
157
});
106
158
107
159
*!*
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
113
161
*/!*
114
162
```
115
163
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`
promise.catch(alert); // shows "Error: Whoops!" after 1 second
175
+
*/!*
176
+
```
117
177
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.
119
179
120
180
## Example: loadScript
121
181
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:
123
185
124
186
```js
125
187
function loadScript(src, callback) {
@@ -135,7 +197,7 @@ function loadScript(src, callback) {
135
197
136
198
Let's rewrite it using promises.
137
199
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`:
139
201
140
202
```js run
141
203
function loadScript(src) {
@@ -159,13 +221,20 @@ promise.then(
159
221
error => alert(`Error: ${error.message}`);
160
222
);
161
223
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!'));
163
225
```
164
226
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.
166
236
167
237
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.
169
238
3. The `promise` object can be passed around, new handlers can be added where needed in other parts of the code.
170
239
171
240
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.
0 commit comments