work
This commit is contained in:
parent
57dc058c49
commit
f57be1bbb3
5 changed files with 221 additions and 46 deletions
15
8-async/03-promise-chaining/01-then-vs-catch/solution.md
Normal file
15
8-async/03-promise-chaining/01-then-vs-catch/solution.md
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
The short answer is: **no, they are not**:
|
||||||
|
|
||||||
|
The difference is that if an error happens in `f1`, then it is handled by `.catch` here:
|
||||||
|
|
||||||
|
```js run
|
||||||
|
promise.then(f1).catch(f2);
|
||||||
|
```
|
||||||
|
|
||||||
|
...But not here:
|
||||||
|
|
||||||
|
```js run
|
||||||
|
promise.then(f1, f2);
|
||||||
|
```
|
||||||
|
|
||||||
|
That's because an error/result is passed down the chain, and in the second code piece there's no chain below.
|
12
8-async/03-promise-chaining/01-then-vs-catch/task.md
Normal file
12
8-async/03-promise-chaining/01-then-vs-catch/task.md
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
# Promise then vs catch
|
||||||
|
|
||||||
|
Are these code fragments equal? In other words, do they behave the same way in any circumstances, for any handler functions?
|
||||||
|
|
||||||
|
```js
|
||||||
|
promise.then(f1, f2);
|
||||||
|
```
|
||||||
|
|
||||||
|
Versus;
|
||||||
|
```js
|
||||||
|
promise.then(f1).catch(f2);
|
||||||
|
```
|
13
8-async/03-promise-chaining/02-error-async/solution.md
Normal file
13
8-async/03-promise-chaining/02-error-async/solution.md
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
The answer is: **no, it won't**:
|
||||||
|
|
||||||
|
```js run
|
||||||
|
new Promise(function(resolve, reject) {
|
||||||
|
setTimeout(() => {
|
||||||
|
throw new Error("Whoops!");
|
||||||
|
}, 1000);
|
||||||
|
}).catch(alert);
|
||||||
|
```
|
||||||
|
|
||||||
|
As said in the chapter, there's an "implicit `try..catch`" around the function code. So all synchronous errors are handled.
|
||||||
|
|
||||||
|
But here the error is generated not while the executor is running, but later. So the promise can't handle it.
|
11
8-async/03-promise-chaining/02-error-async/task.md
Normal file
11
8-async/03-promise-chaining/02-error-async/task.md
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# Error in setTimeout
|
||||||
|
|
||||||
|
How do you think, does the `.catch` trigger? Explain, why?
|
||||||
|
|
||||||
|
```js
|
||||||
|
new Promise(function(resolve, reject) {
|
||||||
|
setTimeout(() => {
|
||||||
|
throw new Error("Whoops!");
|
||||||
|
}, 1000);
|
||||||
|
}).catch(alert);
|
||||||
|
```
|
|
@ -206,18 +206,160 @@ loadScript("/article/promise-chaining/one.js")
|
||||||
|
|
||||||
Once again, the `.catch` handles it.
|
Once again, the `.catch` handles it.
|
||||||
|
|
||||||
**Throwing an exception is also considered an error.**
|
|
||||||
|
|
||||||
For instance:
|
### Implicit try..catch
|
||||||
|
|
||||||
|
Throwing an exception is considered a rejection.
|
||||||
|
|
||||||
|
For instance, this code:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
new Promise(function(resolve, reject) {
|
new Promise(function(resolve, reject) {
|
||||||
|
*!*
|
||||||
throw new Error("Whoops!");
|
throw new Error("Whoops!");
|
||||||
|
*/!*
|
||||||
}).catch(function(error) {
|
}).catch(function(error) {
|
||||||
alert(error.message); // Whoops!
|
alert(error.message); // Whoops!
|
||||||
});
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
...Works the same way as:
|
||||||
|
|
||||||
|
```js run
|
||||||
|
new Promise(function(resolve, reject) {
|
||||||
|
*!*
|
||||||
|
reject(new Error("Whoops!"));
|
||||||
|
*/!*
|
||||||
|
}).catch(function(error) {
|
||||||
|
alert(error.message); // Whoops!
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Like there's an invisible `try..catch` around the whole code of the function, that catches errors.
|
||||||
|
|
||||||
|
That works not only in the executor, but in handlers as well, for instance:
|
||||||
|
|
||||||
|
```js run
|
||||||
|
new Promise(function(resolve, reject) {
|
||||||
|
resolve("ok")
|
||||||
|
}).then(function(result) {
|
||||||
|
*!*
|
||||||
|
throw new Error("Whoops!");
|
||||||
|
*/!*
|
||||||
|
})
|
||||||
|
.catch(function(error) {
|
||||||
|
alert(error.message); // Whoops!
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Rethrowing
|
||||||
|
|
||||||
|
As we already noticed, `.catch` is like `try..catch`. We may have as many `.then` as we want, and then use a single `.catch` at the end to handle errors in all of them.
|
||||||
|
|
||||||
|
In a regular `try..catch` we can analyze the error and maybe rethrow it can't handle. The same thing is possible for promises.
|
||||||
|
|
||||||
|
A handler in `.catch` can finish in two ways:
|
||||||
|
|
||||||
|
1. It can return a value or don't return anything. Then the execution continues "normally", the next `.then(onResolved)` handler is called.
|
||||||
|
2. It can throw an error. Then the execution goes the "error" path, and the closest rejection handler is called.
|
||||||
|
|
||||||
|
Here is an example of the first behavior (the error is handled):
|
||||||
|
|
||||||
|
```js run
|
||||||
|
// the execution: catch -> then
|
||||||
|
new Promise(function(resolve, reject) {
|
||||||
|
|
||||||
|
throw new Error("Whoops!");
|
||||||
|
|
||||||
|
}).catch(function(error) {
|
||||||
|
|
||||||
|
alert("Handled it!");
|
||||||
|
*!*
|
||||||
|
return "result"; // return, the execution goes the "normal way"
|
||||||
|
*/!*
|
||||||
|
|
||||||
|
*!*
|
||||||
|
}).then(alert); // result shown
|
||||||
|
*/!*
|
||||||
|
```
|
||||||
|
|
||||||
|
...And here's an example of "rethrowing":
|
||||||
|
|
||||||
|
|
||||||
|
```js run
|
||||||
|
// the execution: catch -> catch -> then
|
||||||
|
new Promise(function(resolve, reject) {
|
||||||
|
|
||||||
|
throw new Error("Whoops!");
|
||||||
|
|
||||||
|
}).catch(function(error) {
|
||||||
|
|
||||||
|
alert("Can't handle!");
|
||||||
|
*!*
|
||||||
|
throw error; // throwing this or another error jumps to the next catch
|
||||||
|
*/!*
|
||||||
|
|
||||||
|
}).catch(error => {
|
||||||
|
|
||||||
|
alert("Trying to handle again...");
|
||||||
|
// don't return anything => execution goes the normal way
|
||||||
|
|
||||||
|
}).then(alert); // undefined
|
||||||
|
```
|
||||||
|
|
||||||
|
### Unhandled rejections
|
||||||
|
|
||||||
|
What if we forget to handle an error?
|
||||||
|
|
||||||
|
Like here:
|
||||||
|
|
||||||
|
```js untrusted run refresh
|
||||||
|
new Promise(function() {
|
||||||
|
errorHappened(); // Error here (no such function)
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Or here:
|
||||||
|
|
||||||
|
```js untrusted run refresh
|
||||||
|
new Promise(function() {
|
||||||
|
throw new Error("Whoops!");
|
||||||
|
}).then(function() {
|
||||||
|
// ...something...
|
||||||
|
}).then(function() {
|
||||||
|
// ...something else...
|
||||||
|
}).then(function() {
|
||||||
|
// ...but no catch after it!
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Technically, when an error happens, the promise state becomes "rejected", and the execution should jump to the closest rejection handler. But there is none.
|
||||||
|
|
||||||
|
Usually that means that the code is bad. Most JavaScript engines track such situations and generate a global error. 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)):
|
||||||
|
|
||||||
|
|
||||||
|
```js run
|
||||||
|
// open in a new window to see in action
|
||||||
|
|
||||||
|
window.addEventListener('unhandledrejection', function(event) {
|
||||||
|
alert(event.promise); // the promise that generated the error
|
||||||
|
alert(event.reason); // the error itself (Whoops!)
|
||||||
|
});
|
||||||
|
|
||||||
|
new Promise(function() {
|
||||||
|
throw new Error("Whoops!");
|
||||||
|
}).then(function() {
|
||||||
|
// ...something...
|
||||||
|
}).then(function() {
|
||||||
|
// ...something else...
|
||||||
|
}).then(function() {
|
||||||
|
// ...but no catch after it!
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
In non-browser environments there's also a similar event, so we can always track unhandled errors in promises.
|
||||||
|
|
||||||
## Inheriting from promise, thenables, error handling?
|
|
||||||
|
|
||||||
An object that has a method called `.then` is called a "thenable".
|
An object that has a method called `.then` is called a "thenable".
|
||||||
|
|
||||||
|
@ -227,61 +369,43 @@ JavaScript specification also checks the value returned by a handler for being a
|
||||||
|
|
||||||
For instance, native promises give no way to "abort" the execution. The `loadScript` above cannot "cancel" script loading, just because there's no `.abort` method on promises, we can only listen for the state change using `.then/catch`.
|
For instance, native promises give no way to "abort" the execution. The `loadScript` above cannot "cancel" script loading, just because there's no `.abort` method on promises, we can only listen for the state change using `.then/catch`.
|
||||||
|
|
||||||
Let's
|
## Extending promises, thenables
|
||||||
|
|
||||||
|
Promises are very simple by design. One of the thing they miss is the ability to cancel the process.
|
||||||
|
|
||||||
|
For instance, `loadScript(src)` in previous examples returns a promise that allows to track success/failure of the loading. But can we abort it? No.
|
||||||
|
|
||||||
|
We can inherit from `Promise` to introduce such functionality, like this:
|
||||||
|
|
||||||
|
|
||||||
## Error handling
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: NOT WORKING AS INTENDED?
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
new Promise(function(resolve, reject) {
|
function loadScript(src) {
|
||||||
setTimeout(() => resolve(1), 1000);
|
let script = document.createElement('script');
|
||||||
}).then(function(result) {
|
script.src = src;
|
||||||
|
|
||||||
throw new Error("Whoops!");
|
let promise = new Promise(function(resolve, reject) {
|
||||||
|
script.onload = () => resolve(script);
|
||||||
|
*!*
|
||||||
|
script.onerror = err => reject(new Error("Script load error: " + src)); // (*)
|
||||||
|
*/!*
|
||||||
|
});
|
||||||
|
|
||||||
}).catch(function(error) {
|
document.head.append(script);
|
||||||
|
promise.abort = () => script.remove();
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
|
||||||
alert(error.message); // Whoops!
|
let promise = loadScript("/article/promise-chaining/one.js");
|
||||||
|
promise.then(alert);
|
||||||
});
|
promise.abort();
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
The idea is :
|
|
||||||
|
|
||||||
- A callback in `.then` may return a result.
|
|
||||||
|
|
||||||
|
|
||||||
One of main purposes of promises is to make asyn
|
|
||||||
The main purpose of promises
|
|
||||||
Promises
|
|
||||||
|
|
||||||
Promises can be chained. That allows actions to follow one after another.
|
|
||||||
|
|
||||||
Here's a simple example first:
|
|
||||||
|
|
||||||
```js
|
|
||||||
let promise = new Promise(function(resolve, reject) {
|
|
||||||
setTimeout(() => resolve(""))
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
What if we want to
|
## Inheriting from promise, thenables, promise api, async/await
|
||||||
The main idea behind promises
|
|
||||||
Promises can be used for asynchronous tasks that eventually finish with a result or an error.
|
|
||||||
|
|
||||||
We already have `loadScript`
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue