minor
This commit is contained in:
parent
c443777ebb
commit
72edbfb563
2 changed files with 78 additions and 85 deletions
|
@ -453,7 +453,7 @@ alert( sum(1, 2) ); // 3
|
||||||
```smart header="More to come"
|
```smart header="More to come"
|
||||||
Here we praised arrow functions for brevity. But that's not all! Arrow functions have other interesting features. We'll return to them later in the chapter <info:arrow-functions>.
|
Here we praised arrow functions for brevity. But that's not all! Arrow functions have other interesting features. We'll return to them later in the chapter <info:arrow-functions>.
|
||||||
|
|
||||||
For now, we can already use them for one-line actions and callbacks.
|
For now, we can already use arrow functions for one-line actions and callbacks.
|
||||||
```
|
```
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
|
@ -2,65 +2,6 @@
|
||||||
|
|
||||||
There are 5 static methods in the `Promise` class. We'll quickly cover their use cases here.
|
There are 5 static methods in the `Promise` class. We'll quickly cover their use cases here.
|
||||||
|
|
||||||
## Promise.resolve
|
|
||||||
|
|
||||||
The syntax:
|
|
||||||
|
|
||||||
```js
|
|
||||||
let promise = Promise.resolve(value);
|
|
||||||
```
|
|
||||||
|
|
||||||
Returns a resolved promise with the given `value`.
|
|
||||||
|
|
||||||
Same as:
|
|
||||||
|
|
||||||
```js
|
|
||||||
let promise = new Promise(resolve => resolve(value));
|
|
||||||
```
|
|
||||||
|
|
||||||
The method is used when we already have a value, but would like to have it "wrapped" into a promise.
|
|
||||||
|
|
||||||
For instance, the `loadCached` function below fetches the `url` and remembers the result, so that future calls on the same URL return it immediately:
|
|
||||||
|
|
||||||
```js
|
|
||||||
function loadCached(url) {
|
|
||||||
let cache = loadCached.cache || (loadCached.cache = new Map());
|
|
||||||
|
|
||||||
if (cache.has(url)) {
|
|
||||||
*!*
|
|
||||||
return Promise.resolve(cache.get(url)); // (*)
|
|
||||||
*/!*
|
|
||||||
}
|
|
||||||
|
|
||||||
return fetch(url)
|
|
||||||
.then(response => response.text())
|
|
||||||
.then(text => {
|
|
||||||
cache.set(url,text);
|
|
||||||
return text;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
We can use `loadCached(url).then(…)`, because the function is guaranteed to return a promise. That's the purpose `Promise.resolve` serves in the line `(*)`: it makes sure the interface is unified. We can always use `.then` after `loadCached`.
|
|
||||||
|
|
||||||
## Promise.reject
|
|
||||||
|
|
||||||
The syntax:
|
|
||||||
|
|
||||||
```js
|
|
||||||
let promise = Promise.reject(error);
|
|
||||||
```
|
|
||||||
|
|
||||||
Create a rejected promise with the `error`.
|
|
||||||
|
|
||||||
Same as:
|
|
||||||
|
|
||||||
```js
|
|
||||||
let promise = new Promise((resolve, reject) => reject(error));
|
|
||||||
```
|
|
||||||
|
|
||||||
We cover it here for completeness, rarely used in real code.
|
|
||||||
|
|
||||||
## Promise.all
|
## Promise.all
|
||||||
|
|
||||||
Let's say we want to run many promises to execute in parallel, and wait till all of them are ready.
|
Let's say we want to run many promises to execute in parallel, and wait till all of them are ready.
|
||||||
|
@ -75,9 +16,9 @@ The syntax is:
|
||||||
let promise = Promise.all([...promises...]);
|
let promise = Promise.all([...promises...]);
|
||||||
```
|
```
|
||||||
|
|
||||||
It takes an array of promises (technically can be any iterable, but usually an array) and returns a new promise.
|
`Promise.all` takes an array of promises (technically can be any iterable, but usually an array) and returns a new promise.
|
||||||
|
|
||||||
The new promise resolves when all listed promises are settled and has an array of their results.
|
The new promise resolves when all listed promises are settled and the array of their results becomes its result.
|
||||||
|
|
||||||
For instance, the `Promise.all` below settles after 3 seconds, and then its result is an array `[1, 2, 3]`:
|
For instance, the `Promise.all` below settles after 3 seconds, and then its result is an array `[1, 2, 3]`:
|
||||||
|
|
||||||
|
@ -89,7 +30,7 @@ Promise.all([
|
||||||
]).then(alert); // 1,2,3 when promises are ready: each promise contributes an array member
|
]).then(alert); // 1,2,3 when promises are ready: each promise contributes an array member
|
||||||
```
|
```
|
||||||
|
|
||||||
Please note that the relative order is the same. Even though the first promise takes the longest time to resolve, it is still first in the array of results.
|
Please note that the order of resulting array members is the same as source promises. Even though the first promise takes the longest time to resolve, it's still first in the array of results.
|
||||||
|
|
||||||
A common trick is to map an array of job data into an array of promises, and then wrap that into `Promise.all`.
|
A common trick is to map an array of job data into an array of promises, and then wrap that into `Promise.all`.
|
||||||
|
|
||||||
|
@ -121,7 +62,7 @@ let requests = names.map(name => fetch(`https://api.github.com/users/${name}`));
|
||||||
|
|
||||||
Promise.all(requests)
|
Promise.all(requests)
|
||||||
.then(responses => {
|
.then(responses => {
|
||||||
// all responses are ready, we can show HTTP status codes
|
// all responses are resolved successfully
|
||||||
for(let response of responses) {
|
for(let response of responses) {
|
||||||
alert(`${response.url}: ${response.status}`); // shows 200 for every url
|
alert(`${response.url}: ${response.status}`); // shows 200 for every url
|
||||||
}
|
}
|
||||||
|
@ -159,7 +100,7 @@ For example, if there are multiple `fetch` calls, like in the example above, and
|
||||||
```
|
```
|
||||||
|
|
||||||
````smart header="`Promise.all(iterable)` allows non-promise \"regular\" values in `iterable`"
|
````smart header="`Promise.all(iterable)` allows non-promise \"regular\" values in `iterable`"
|
||||||
Normally, `Promise.all(...)` accepts an iterable (in most cases an array) of promises. But if any of those objects is not a promise, it's wrapped in `Promise.resolve`.
|
Normally, `Promise.all(...)` accepts an iterable (in most cases an array) of promises. But if any of those objects is not a promise, it's passed to the resulting array "as is".
|
||||||
|
|
||||||
For instance, here the results are `[1, 2, 3]`:
|
For instance, here the results are `[1, 2, 3]`:
|
||||||
|
|
||||||
|
@ -168,8 +109,8 @@ Promise.all([
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
setTimeout(() => resolve(1), 1000)
|
setTimeout(() => resolve(1), 1000)
|
||||||
}),
|
}),
|
||||||
2, // treated as Promise.resolve(2)
|
2,
|
||||||
3 // treated as Promise.resolve(3)
|
3
|
||||||
]).then(alert); // 1, 2, 3
|
]).then(alert); // 1, 2, 3
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -180,22 +121,22 @@ So we are able to pass ready values to `Promise.all` where convenient.
|
||||||
|
|
||||||
[recent browser="new"]
|
[recent browser="new"]
|
||||||
|
|
||||||
`Promise.all` rejects as a whole if any promise rejects. That's good in cases, when we need *all* results to go on:
|
`Promise.all` rejects as a whole if any promise rejects. That's good for "all or nothing" cases, when we need *all* results to go on:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
Promise.all([
|
Promise.all([
|
||||||
fetch('/template.html'),
|
fetch('/template.html'),
|
||||||
fetch('/style.css'),
|
fetch('/style.css'),
|
||||||
fetch('/data.json')
|
fetch('/data.json')
|
||||||
]).then(render); // render method needs them all
|
]).then(render); // render method needs results of all fetches
|
||||||
```
|
```
|
||||||
|
|
||||||
`Promise.allSettled` waits for all promises to settle: even if one rejects, it waits for the others. The resulting array has:
|
`Promise.allSettled` waits for all promises to settle. The resulting array has:
|
||||||
|
|
||||||
- `{status:"fulfilled", value:result}` for successful responses,
|
- `{status:"fulfilled", value:result}` for successful responses,
|
||||||
- `{status:"rejected", reason:error}` for errors.
|
- `{status:"rejected", reason:error}` for errors.
|
||||||
|
|
||||||
For example, we'd like to fetch the information about multiple users. Even if one request fails, we're interested in the others.
|
For example, we'd like to fetch the information about multiple users. Even if one request fails, we're still interested in the others.
|
||||||
|
|
||||||
Let's use `Promise.allSettled`:
|
Let's use `Promise.allSettled`:
|
||||||
|
|
||||||
|
@ -228,7 +169,7 @@ The `results` in the line `(*)` above will be:
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
So, for each promise we get its status and `value/reason`.
|
So, for each promise we get its status and `value/error`.
|
||||||
|
|
||||||
### Polyfill
|
### Polyfill
|
||||||
|
|
||||||
|
@ -237,18 +178,18 @@ If the browser doesn't support `Promise.allSettled`, it's easy to polyfill:
|
||||||
```js
|
```js
|
||||||
if(!Promise.allSettled) {
|
if(!Promise.allSettled) {
|
||||||
Promise.allSettled = function(promises) {
|
Promise.allSettled = function(promises) {
|
||||||
return Promise.all(promises.map(p => Promise.resolve(p).then(v => ({
|
return Promise.all(promises.map(p => Promise.resolve(p).then(value => ({
|
||||||
state: 'fulfilled',
|
state: 'fulfilled',
|
||||||
value: v,
|
value
|
||||||
}), r => ({
|
}), reason => ({
|
||||||
state: 'rejected',
|
state: 'rejected',
|
||||||
reason: r,
|
reason
|
||||||
}))));
|
}))));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
In this code, `promises.map` takes input values, turns into promises (just in case a non-promise was passed) with `p => Promise.resolve(p)`, and then adds `.then` handler to it.
|
In this code, `promises.map` takes input values, turns into promises (just in case a non-promise was passed) with `p => Promise.resolve(p)`, and then adds `.then` handler to every one.
|
||||||
|
|
||||||
That handler turns a successful result `v` into `{state:'fulfilled', value:v}`, and an error `r` into `{state:'rejected', reason:r}`. That's exactly the format of `Promise.allSettled`.
|
That handler turns a successful result `v` into `{state:'fulfilled', value:v}`, and an error `r` into `{state:'rejected', reason:r}`. That's exactly the format of `Promise.allSettled`.
|
||||||
|
|
||||||
|
@ -256,7 +197,7 @@ Then we can use `Promise.allSettled` to get the results or *all* given promises,
|
||||||
|
|
||||||
## Promise.race
|
## Promise.race
|
||||||
|
|
||||||
Similar to `Promise.all`, it takes an iterable of promises, but instead of waiting for all of them to finish, it waits for the first result (or error), and goes on with it.
|
Similar to `Promise.all`, but waits only for the first settled promise, and gets its result (or error).
|
||||||
|
|
||||||
The syntax is:
|
The syntax is:
|
||||||
|
|
||||||
|
@ -274,18 +215,70 @@ Promise.race([
|
||||||
]).then(alert); // 1
|
]).then(alert); // 1
|
||||||
```
|
```
|
||||||
|
|
||||||
So, the first result/error becomes the result of the whole `Promise.race`. After the first settled promise "wins the race", all further results/errors are ignored.
|
The first promise here was fastest, so it became the result. After the first settled promise "wins the race", all further results/errors are ignored.
|
||||||
|
|
||||||
|
|
||||||
|
## Promise.resolve/reject
|
||||||
|
|
||||||
|
Methods `Promise.resolve` and `Promise.reject` are rarely needed in modern code, because `async/await` syntax (we'll cover it in [a bit later](info:async-await)) makes them somewhat obsolete.
|
||||||
|
|
||||||
|
We cover them here for completeness, and for those who can't use `async/await` for some reason.
|
||||||
|
|
||||||
|
- `Promise.resolve(value)` creates a resolved promise with the result `value`.
|
||||||
|
|
||||||
|
Same as:
|
||||||
|
|
||||||
|
```js
|
||||||
|
let promise = new Promise(resolve => resolve(value));
|
||||||
|
```
|
||||||
|
|
||||||
|
The method is used for compatibility, when a function is expected to return a promise.
|
||||||
|
|
||||||
|
For example, `loadCached` function below fetches URL and remembers (caches) its content. For future calls with the same URL it immediately gets the previous content from cache, but uses `Promise.resolve` to make a promise of it, so that the returned value is always a promise:
|
||||||
|
|
||||||
|
```js
|
||||||
|
let cache = new Map();
|
||||||
|
|
||||||
|
function loadCached(url) {
|
||||||
|
if (cache.has(url)) {
|
||||||
|
*!*
|
||||||
|
return Promise.resolve(cache.get(url)); // (*)
|
||||||
|
*/!*
|
||||||
|
}
|
||||||
|
|
||||||
|
return fetch(url)
|
||||||
|
.then(response => response.text())
|
||||||
|
.then(text => {
|
||||||
|
cache.set(url,text);
|
||||||
|
return text;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
We can write `loadCached(url).then(…)`, because the function is guaranteed to return a promise. We can always use `.then` after `loadCached`. That's the purpose of `Promise.resolve` in the line `(*)`.
|
||||||
|
|
||||||
|
### Promise.reject
|
||||||
|
|
||||||
|
- `Promise.reject(error)` creates a rejected promise with `error`.
|
||||||
|
|
||||||
|
Same as:
|
||||||
|
|
||||||
|
```js
|
||||||
|
let promise = new Promise((resolve, reject) => reject(error));
|
||||||
|
```
|
||||||
|
|
||||||
|
In practice, this method is almost never used.
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
There are 5 static methods of `Promise` class:
|
There are 5 static methods of `Promise` class:
|
||||||
|
|
||||||
1. `Promise.resolve(value)` -- makes a resolved promise with the given value.
|
1. `Promise.all(promises)` -- waits for all promises to resolve and returns an array of their results. If any of the given promises rejects, then it becomes the error of `Promise.all`, and all other results are ignored.
|
||||||
2. `Promise.reject(error)` -- makes a rejected promise with the given error.
|
2. `Promise.allSettled(promises)` (recently added method) -- waits for all promises to settle and returns their results as array of objects with:
|
||||||
3. `Promise.all(promises)` -- waits for all promises to resolve and returns an array of their results. If any of the given promises rejects, then it becomes the error of `Promise.all`, and all other results are ignored.
|
- `state`: `"fulfilled"` or `"rejected"`
|
||||||
4. `Promise.allSettled(promises)` (a new method) -- waits for all promises to resolve or reject and returns an array of their results as object with:
|
|
||||||
- `state`: `'fulfilled'` or `'rejected'`
|
|
||||||
- `value` (if fulfilled) or `reason` (if rejected).
|
- `value` (if fulfilled) or `reason` (if rejected).
|
||||||
5. `Promise.race(promises)` -- waits for the first promise to settle, and its result/error becomes the outcome.
|
3. `Promise.race(promises)` -- waits for the first promise to settle, and its result/error becomes the outcome.
|
||||||
|
4. `Promise.resolve(value)` -- makes a resolved promise with the given value.
|
||||||
|
5. `Promise.reject(error)` -- makes a rejected promise with the given error.
|
||||||
|
|
||||||
Of these five, `Promise.all` is probably the most common in practice.
|
Of these five, `Promise.all` is probably the most common in practice.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue