better async

This commit is contained in:
Ilya Kantor 2017-05-24 01:03:26 +03:00
parent e456067d3d
commit 0ad6ba01b4
14 changed files with 584 additions and 55 deletions

View file

@ -113,8 +113,8 @@ let promise = new Promise(function(resolve, reject) {
For instance, it happens when we start to do a job and then see that everything has already been done. Technically that's fine: we have a resolved promise right now.
````
````smart header="Promise properties are internal"
Properties of promise objects like `state` and `result` are internal. We can't directly access them from our code.
```smart header="The `state` and `result` are internal"
Properties `state` and `result` of a promise object are internal. We can't directly access them from our code, but we can use methods `.then/catch` for that, they are described below.
```
## Consumers: ".then" and ".catch"
@ -200,10 +200,25 @@ If a promise is pending, `.then/catch` handlers wait for the result. Otherwise,
// an immediately resolved promise
let promise = new Promise(resolve => resolve("done!"));
promise.then(alert); // done! (without a delay)
promise.then(alert); // done! (shows up right now)
```
That's good for jobs that may sometimes require time and sometimes finish immediately. The handler is guaranteed to run in both cases.
To be precise, `.then/catch` queue up and are taken from the queue asynchronously when the current code finishes, like `setTimeout(..., 0)`.
So here the `alert` call is "queued" and runs immediately after the code finishes:
```js run
// an immediately resolved promise
let promise = new Promise(resolve => resolve("done!"));
promise.then(alert); // done! (right after the current code finishes)
alert("code finished"); // this alert shows first
```
In practice the time for the code to finish execution is usually negligible. But the code after `.then` always executes before the `.then` handler (even in the case of a pre-resolved promise), that could matter.
````
Now let's see more practical examples to see how promises can help us in writing asynchronous code.

View file

@ -189,9 +189,44 @@ This code does the same: loads 3 scripts in sequence. But it "grows to the right
Sometimes it's ok to write `.then` directly, because the nested function has access to the outer scope `(*)`, but that's an exception rather than a rule.
````smart header="Thenables"
To be precise, `.then` may return an arbitrary "thenable" object, and it will be treated the same way as a promise.
A "thenable" object is any object with a method `.then`.
The idea is that 3rd-party libraries may implement "promise-compatible" objects of their own. They can have extended set of methods, but also be compatible with native promises, because they implement `.then`.
Here's an example of a thenable object:
```js run
class Thenable {
constructor(result, delay) {
this.result = result;
}
// then method has the similar signature to promises
then(resolve, reject) {
// resolve with double this.result after the delay
setTimeout(() => resolve(this.result * 2), delay);
}
};
new Promise(resolve => resolve(1))
.then(result => {
return new Thenable(result, 1000); // (*)
})
.then(alert); // shows 2 after 1000ms
```
JavaScript checks the object returned by the handler in the line `(*)`: it it has a callable method named `then`, then it waits until that method is called, and the result is passed further.
That allows to integrate side objects with promise chains without having to inherit from `Promise`.
````
## Bigger example: fetch
In frontend programming promises are often used for network requests. So let's make an example of that.
In frontend programming promises are often used for network requests. So let's see an extended example of that.
We'll use the [fetch](mdn:api/WindowOrWorkerGlobalScope/fetch) method to load the information about the user from the remote server. The method is quite complex, it has many optional parameters, but the basic usage is quite simple:
@ -199,17 +234,18 @@ We'll use the [fetch](mdn:api/WindowOrWorkerGlobalScope/fetch) method to load th
let promise = fetch(url);
```
Makes a network request to the `url` and returns a promise. The promise resolves with a `response` object when the remote server responds with headers, but before the full response is downloaded.
This makes a network request to the `url` and returns a promise. The promise resolves with a `response` object when the remote server responds with headers, but before the full response is downloaded.
To read the full response, we should call a method `response.text()`: it returns a promise that resolves with the full response text when it's downloaded from the remote server.
To read the full response, we should call a method `response.text()`: it returns a promise that resolves when the full text downloaded from the remote server, and has that text as a result.
The code below makes a request to `user.json` and then loads it as text from the server:
The code below makes a request to `user.json` and loads its text from the server:
```js run
fetch('/article/promise-chaining/user.json')
// .then runs when the remote server responds
// .then below runs when the remote server responds
.then(function(response) {
// response.text() is the new promise that resolves when the server finishes sending data
// response.text() returns a new promise that resolves with the full response text
// when we finish downloading it
return response.text();
})
.then(function(text) {
@ -223,12 +259,15 @@ There is also a method `response.json()` that reads the remote data and parses i
We'll also use arrow functions for brevity:
```js run
// same as above, but response.json() parses the remote content as JSON
fetch('/article/promise-chaining/user.json')
.then(response => response.json())
.then(user => alert(user.name)); // iliakan
```
Now let's do something with it. For instance, we can make one more request to github, load the user profile and show the avatar:
Now let's do something with the loaded user.
For instance, we can make one more request to github, load the user profile and show the avatar:
```js run
// 1. Make a request for user.json
@ -250,9 +289,9 @@ fetch('/article/promise-chaining/user.json')
});
```
The code works. But there's a potential problem in it.
The code works. But there's a potential problem in it, a typical error of those who begin to use promises.
Look at the line `(*)`: how can we do something *after* the avatar is removed? For instance, we'd like to show a form for editing that user or something else.
Look at the line `(*)`: how can we do something *after* the avatar is removed? For instance, we'd like to show a form for editing that user or something else. As of now, there's no way.
To make the chain extendable, we need to return a promise that resolves when the avatar finishes showing.
@ -265,6 +304,7 @@ fetch('/article/promise-chaining/user.json')
.then(response => response.json())
*!*
.then(githubUser => new Promise(function(resolve, reject) {
*/!*
let img = document.createElement('img');
img.src = githubUser.avatar_url;
img.className = "promise-avatar-example";
@ -272,17 +312,20 @@ fetch('/article/promise-chaining/user.json')
setTimeout(() => {
img.remove();
*!*
resolve(githubUser);
*/!*
}, 3000);
}))
*/!*
// triggers after 3 seconds
.then(githubUser => alert(`Finished showing ${githubUser.name}`));
```
Now when `setTimeout` runs the function, it calls `resolve(githubUser)`, thus passing the control to the next `.then` in the chain and passing forward the user data.
As a rule, an asynchronous action should always return a promise. That makes possible to plan actions after it. Even if we don't plan to extend the chain now, we may need it later.
As a rule, an asynchronous action should always return a promise.
That makes possible to plan actions after it. Even if we don't plan to extend the chain now, we may need it later.
Finally, we can split the code into reusable functions:
@ -311,6 +354,7 @@ function showAvatar(githubUser) {
});
}
// Use them:
loadJson('/article/promise-chaining/user.json')
.then(user => loadGithubUser(user.name))
.then(showAvatar)
@ -476,15 +520,102 @@ new Promise(function(resolve, reject) {
The handler `(*)` catches the error and just can't handle it, because it's not `URIError`, so it throws it again. Then the execution jumps to the next `.catch` down the chain `(**)`.
In the section below we'll see a practical example of rethrowing.
## Fetch error handling example
Let's improve error handling for the user-loading example.
The promise returned by [fetch](mdn:api/WindowOrWorkerGlobalScope/fetch) rejects when it's impossible to make a request. For instance, a remote server is not available, or the URL is malformed. But if the remote server responds with error 404, or even error 500, then it's considered a valid response.
What if the server returns a non-JSON page with error 500 in the line `(*)`? What if there's no such user, and github returns a page with error 404 at `(**)`?
```js run
fetch('no-such-user.json') // (*)
.then(response => response.json())
.then(user => fetch(`https://api.github.com/users/${user.name}`)) // (**)
.then(response => response.json())
.catch(alert); // SyntaxError: Unexpected token < in JSON at position 0
// ...
```
As of now, the code tries to load the response as JSON no matter what and dies with a syntax error. You can see that by running the example above, as the file `no-such-user.json` doesn't exist.
That's not good, because the error just falls through the chain, without details: what failed and where.
So let's add one more step: we should check the `response.status` property that has HTTP status, and if it's not 200, then throw an error.
```js run
class HttpError extends Error { // (1)
constructor(response) {
super(`${response.status} for ${response.url}`);
this.name = 'HttpError';
this.response = response;
}
}
function loadJson(url) { // (2)
return fetch(url)
.then(response => {
if (response.status == 200) {
return response.json();
} else {
throw new HttpError(response);
}
})
}
loadJson('no-such-user.json') // (3)
.catch(alert); // HttpError: 404 for .../no-such-user.json
```
1. We make a custom class for HTTP Errors to distinguish them from other types of errors. Besides, the new class has a constructor that accepts the `response` object and saves it in the error. So error-handling code will be able to access it.
2. Then we put together the requesting and error-handling code into a function that fetches the `url` *and* treats any non-200 status as an error. That's convenient, because we often need such logic.
3. Now `alert` shows better message.
The great thing about having our own class for errors is that we can easily check for it in error-handling code.
For instance, we can make a request, and then if we get 404 -- ask the user to modify the information.
The code below loads a user with the given name from github. If there's no such user, then it asks for the correct name:
```js run
function demoGithubUser() {
let name = prompt("Enter a name?", "iliakan");
return loadJson(`https://api.github.com/users/${name}`)
.then(user => {
alert(`Full name: ${user.name}.`); // (1)
return user;
})
.catch(err => {
*!*
if (err instanceof HttpError && err.response.status == 404) { // (2)
*/!*
alert("No such user, please reenter.");
return demoGithubUser();
} else {
throw err;
}
});
}
demoGithubUser();
```
Here:
1. If `loadJson` returns a valid user object, then the name is shown `(1)`, and the user is returned, so that we can add more user-related actions to the chain. In that case the `.catch` below is ignored, everything's very simple and fine.
2. Otherwise, in case of an error, we check it in the line `(2)`. Only if it's indeed the HTTP error, and the status is 404 (Not found), we ask the user to reenter. For other errors -- we don't know how to handle, so we just rethrow them.
## Unhandled rejections
...But what if we forget to append an error handler to the end of the chain?
Like here:
What happens when an error is not handled? For instance, after the rethrow as in the example above. Or if we forget to append an error handler to the end of the chain, like here:
```js untrusted run refresh
new Promise(function() {
errorHappened(); // Error here (no such function)
noSuchFunction(); // Error here (no such function)
});
```
@ -502,32 +633,29 @@ new Promise(function() {
});
```
Technically, when an error happens, the promise state becomes "rejected", and the execution should jump to the closest rejection handler. But there is no such handler in the examples above.
In theory, nothing should happen. In case of an error happens, the promise state becomes "rejected", and the execution should jump to the closest rejection handler. But there is no such handler in the examples above. So the error gets "stuck".
Usually that means that the code is bad. Indeed, how come that there's no error handling?
Most JavaScript engines track such situations and generate a global error in that case. In the browser we can catch it using `window.addEventListener('unhandledrejection')` as specified in the [HTML standard](https://html.spec.whatwg.org/multipage/webappapis.html#unhandled-promise-rejections):
In practice, that means that the code is bad. Indeed, how come that there's no error handling?
Most JavaScript engines track such situations and generate a global error in that case. In the browser we can catch it using the event `unhandledrejection`:
```js run
*!*
window.addEventListener('unhandledrejection', function(event) {
// the event object has two special properties:
alert(event.promise); // [object Promise] - the promise that generated the error
alert(event.reason); // Error: Whoops! - the unhandled error object
});
*/!*
new Promise(function() {
throw new Error("Whoops!");
}).then(function() {
// ...something...
}).then(function() {
// ...something else...
}).then(function() {
// ...but no catch after it!
});
}); // no catch to handle the error
```
Now if an error has occured, and there's no `.catch`, the event `unhandledrejection` triggers, and our handler can do something with the exception. Once again, such situation is usually a programming error.
The event is the part of the [HTML standard](https://html.spec.whatwg.org/multipage/webappapis.html#unhandled-promise-rejections). Now if an error occurs, and there's no `.catch`, the `unhandledrejection` handler triggers: the `event` object has the information about the error, so we can do something with it.
Usually such errors are unrecoverable, so our best way out is to inform the user about the problem and probably report about the incident to the server.
In non-browser environments like Node.JS there are other similar ways to track unhandled errors.
@ -549,6 +677,6 @@ The smaller picture of how handlers are called:
In the examples of error handling above the `.catch` was always the last in the chain. In practice though, not every promise chain has a `.catch`. Just like regular code is not always wrapped in `try..catch`.
We should place `.catch` exactly in the places where we want to handle errors and know how to handle them.
We should place `.catch` exactly in the places where we want to handle errors and know how to handle them. Using custom error classes can help to analyze errors and rethrow those that we can't handle.
For errors that are outside of that scope we should have the `unhandledrejection` event handler. Such unknown errors are usually unrecoverable, so all we should do is to inform the user and probably report to our server about the incident.
For errors that fall outside of our scope we should have the `unhandledrejection` event handler (for browsers, and analogs for other environments). Such unknown errors are usually unrecoverable, so all we should do is to inform the user and probably report to our server about the incident.

View file

@ -10,6 +10,25 @@ function loadScript(src) {
document.head.append(script);
});
}
class HttpError extends Error {
constructor(response) {
super(`${response.status} for ${response.url}`);
this.name = 'HttpError';
this.response = response;
}
}
function loadJson(url) {
return fetch(url)
.then(response => {
if (response.status == 200) {
return response.json();
} else {
throw new HttpError(response);
}
})
}
</script>
<style>

View file

@ -0,0 +1,44 @@
The solution is actually pretty simple.
Take a look at this:
```js
Promise.all(
fetch('https://api.github.com/users/iliakan'),
fetch('https://api.github.com/users/remy'),
fetch('http://no-such-url')
)
```
Here we have an array of `fetch(...)` promises that goes to `Promise.all`.
We can't change the way `Promise.all` works: if it detects an error, then it rejects with it. So we need to prevent any error from occuring. Instead, if a `fetch` error happens, we need to treat it as a "normal" result.
Here's how:
```js
Promise.all(
fetch('https://api.github.com/users/iliakan').catch(err => err),
fetch('https://api.github.com/users/remy').catch(err => err),
fetch('http://no-such-url').catch(err => err)
)
```
In other words, the `.catch` takes an error for all of the promises and returns it normally. By the rules of how promises work, if a `.then/catch` handler returns a value (doesn't matter if it's an error object or something else), then the execution continues the "normal" flow.
So the `.catch` returns the error as a "normal" result into the outer `Promise.all`.
This code:
```js
Promise.all(
urls.map(url => fetch(url))
)
```
Can be rewritten as:
```js
Promise.all(
urls.map(url => fetch(url).catch(err => err))
)
```

View file

@ -0,0 +1,24 @@
<!DOCTYPE HTML>
<html>
<body>
<script>
let urls = [
'https://api.github.com/users/iliakan',
'https://api.github.com/users/remy',
'http://no-such-url'
];
Promise.all(
urls.map(url => fetch(url).catch(err => err))
)
.then(responses => {
alert(responses[0].status); // 200
alert(responses[1].status); // 200
alert(responses[2]); // TypeError: failed to fetch (text may vary)
});
</script>
</body>
</html>

View file

@ -0,0 +1,28 @@
<!DOCTYPE HTML>
<html>
<body>
<script>
// if any of URLs fails, other results are ignored
// change that:
// make errors appear as members of the responses array, together with normal results
let urls = [
'https://api.github.com/users/iliakan',
'https://api.github.com/users/remy',
'http://no-such-url'
];
// Fix me:
Promise.all(urls.map(url => fetch(url)))
// Demo output (no need to change):
.then(responses => {
alert(responses[0].status); // 200
alert(responses[1].status); // 200
alert(responses[2]); // TypeError: failed to fetch (text may vary)
));
</script>
</body>
</html>

View file

@ -0,0 +1,48 @@
# Fault-tolerant Promise.all
We'd like to fetch multiple URLs in parallel.
Here's the code to do that:
```js run
let urls = [
'https://api.github.com/users/iliakan',
'https://api.github.com/users/remy',
'https://api.github.com/users/jeresig'
];
Promise.all(urls.map(url => fetch(url)))
// for each response show its status
.then(responses => { // (*)
for(let response of responses) {
alert(`${response.url}: ${response.status}`);
}
));
```
The problem is that if any of requests fails, then `Promise.all` rejects with the error, and we loose results of all the other requests.
That's not good.
Modify the code so that the array `responses` in the line `(*)` would include the response objects for successful fetches and error objects for failed ones.
For instance, if one of URLs is bad, then it should be like:
```js
let urls = [
'https://api.github.com/users/iliakan',
'https://api.github.com/users/remy',
'http://no-such-url'
];
Promise.all(...) // your code to fetch URLs...
// ...and pass fetch errors as members of the resulting array...
.then(responses => {
// 3 urls => 3 array members
alert(responses[0].status); // 200
alert(responses[1].status); // 200
alert(responses[2]); // TypeError: failed to fetch (text may vary)
});
```
P.S. In this task you don't have to load the full response using `response.text()` or `response.json()`. Just handle fetch errors the right way.

View file

@ -0,0 +1,29 @@
<!DOCTYPE HTML>
<html>
<body>
<script>
let urls = [
'https://api.github.com/users/iliakan',
'/',
'http://no-such-url'
];
Promise.all(
urls.map(url => fetch(url).catch(err => err))
)
.then(responses => Promise.all(
// if it's an error then pass on
// otherwise response.json() and catch errors as results
responses.map(r => r instanceof Error ? r : r.json().catch(err => err))
))
.then(results => {
alert(results[0].name); // Ilya Kantor
alert(results[1]); // SyntaxError: Unexpected token < in JSON at position 0
alert(results[2]); // TypeError: failed to fetch (text may vary)
});
</script>
</body>
</html>

View file

@ -0,0 +1,33 @@
<!DOCTYPE HTML>
<html>
<body>
<script>
// the whole promise chain fails with an error here
// change that:
// make errors appear as members of the results array
let urls = [
'https://api.github.com/users/iliakan',
// this URL is HTML page, it's invalid JSON, so response.json() fails
'/',
// this URL is invalid, so fetch fails
'http://no-such-url'
];
// Fix it:
Promise.all(urls.map(url => fetch(url)))
.then(responses => Promise.all(
responses.map(r => r.json())
))
// Demo output (no need to change):
.then(results => {
alert(results[0].name); // Ilya Kantor
alert(results[1]); // SyntaxError: Unexpected token < in JSON at position 0
alert(results[2]); // TypeError: failed to fetch (text may vary)
});
</script>
</body>
</html>

View file

@ -0,0 +1,34 @@
# Fault-tolerant fetch with JSON
Improve the solution of the previous task <info:task/promise-errors-as-results>. Now we need not just to call `fetch`, but to load the JSON objects from given URLs.
Here's the example code to do that:
```js run
let urls = [
'https://api.github.com/users/iliakan',
'https://api.github.com/users/remy',
'https://api.github.com/users/jeresig'
];
// make fetch requests
Promise.all(urls.map(url => fetch(url)))
// map each response to response.json()
.then(responses => Promise.all(
responses.map(r => r.json())
))
// show name of each user
.then(users => { // (*)
for(let user of users) {
alert(user.name);
}
});
```
The problem is that if any of requests fails, then `Promise.all` rejects with the error, and we loose results of all the other requests. So the code above is not fault-tolerant, just like the one in the previous task.
Modify the code so that the array in the line `(*)` would include parsed JSON for successful requests and error for errored ones.
Please note that the error may occur both in `fetch` (if the network request fails) and in `response.json()` (if the response is invalid JSON). In both cases the error should become a member of the results object.
The sandbox has both of these cases.

View file

@ -1,6 +1,65 @@
# Promise API
There are helpful static methods in the `Promise` class. There are only 4 of them, so we'll quickly cover them here.
There are 4 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[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` in the line `(*)`: it makes sure the interface 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
@ -12,42 +71,109 @@ The syntax is:
let promise = Promise.all(iterable);
```
It takes an `iterable` object with promises, for instance an array and returns a new promise that resolves with the array of their results when all of them are settled, or rejects with the first encountered error if any.
It takes an `iterable` object with promises, for instance an array and returns a new promise that resolves with when all of them are settled and has an array of results.
For instance, the `Promise.all` below settles after 3 seconds, and the result is an array `[1, 2, 3]`:
```js run
Promise.all([
new Promise((resolve, reject) => setTimeout(() => resolve(1), 3000)),
new Promise((resolve, reject) => setTimeout(() => resolve(2), 2000)),
new Promise((resolve, reject) => setTimeout(() => resolve(3), 1000))
]).then(alert); // 1,2,3 after all promises are ready
```
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.
A more real-life example with fetching user information for an array of github users by their names:
```js run
let names = ['iliakan', 'remy', 'jresig'];
// map every url to the promise fetch(github url)
let requests = names.map(name => fetch(`https://api.github.com/users/${name}`));
// Promise.all waits until all jobs are resolved
Promise.all(requests)
.then(responses => {
// all responses are ready, we can show HTTP status codes
for(let response of responses) {
alert(`${response.url}: ${response.status}`); // shows 200 for every url
}
return responses;
})
// map array of responses into array of response.json() and wrap them info Promise.all
.then(responses => Promise.all(responses.map(r => r.json())))
// all JSON answers are parsed: users is the array of them
.then(users => users.forEach(user => alert(user.name)));
```
If any of the promises is rejected, `Promise.all` also rejects with that error.
For instance:
```js run
// loads 3 scripts in parallel and returns an array of them
Promise.all([
loadScript('/article/promise-api/one.js'),
loadScript('/article/promise-api/two.js'),
loadScript('/article/promise-api/three.js')
]).then(scripts => {
alert(`scripts loaded: ${scripts}`);
});
new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),
*!*
new Promise((resolve, reject) => setTimeout(() => reject(new Error("Whoops!")), 2000)),
*/!*
new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))
]).catch(alert); // Error: Whoops!
```
Here the `.catch` runs after 2 seconds, when the second promise rejects, and the rejection error becomes the outcome of the whole `Promise.all`.
The important detail is that promises provide no way to "cancel" or "abort" their execution. So in the example above all promises finally settle, but in case of an error all results are ignored, "thrown away".
- the returned `promise` awaits for al
````smart header="`Promise.all` wraps non-promise arguments into `Promise.resolve`"
Normally, `Promise.all(iterable)` accepts an iterable of promise objects. But if any of those objects is not a promise, it's wrapped in `Promise.resolve`.
In the previous chapter we saw how to run things sequentially. Promises also
For instance, here the results are `[1, 2, 3]`:
- Promise.all
- Promise.race
- Promise.reject
- Promise.resolve
```js run
Promise.all([
new Promise((resolve, reject) => {
setTimeout(() => resolve(1), 1000)
}),
2, // treated as Promise.resolve(2)
3 // treated as Promise.resolve(3)
]).then(alert); // 1, 2, 3
```
Let's meet more functions and methods for promises.
So we are able to pass non-promise values to `Promise.all` where convenient.
````
## Promise.race
Keywords `async` and `await` provide a more elegant way to write the code using promises.
Similar to `Promise.all` takes an iterable of promises, but instead of waiting for all of them to finish -- waits for the first result (or error), and goes on with it.
## Async functions
The syntax is:
The `async` function is like a regular one, but it wraps a returned value in a `Promise`.
```js
let promise = Promise.race(iterable);
```
For instance, here the result will be `1`:
```js run
Promise.race([
new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),
new Promise((resolve, reject) => setTimeout(() => reject(new Error("Whoops!")), 2000)),
new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))
]).then(alert); // 1
```
Nowadays, promises are de-facto standard for asynchronous actions, when we need to
So, the first result/error becomes the result of the whole `Promise.race`, and further results/errors are ignored.
## Summary
There are 4 static methods of `Promise` class:
1. `Promise.resolve(value)` -- makes a resolved promise with the given value,
2. `Promise.reject(error)` -- makes a rejected promise with the given error,
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.
4. `Promise.race(promises)` -- waits for the first promise to settle, and its result/error becomes the outcome.

View file

@ -0,0 +1,4 @@
{
"name": "iliakan",
"isAdmin": true
}

View file

@ -1,3 +0,0 @@
function three() {
alert(3);
}