Skip to content

Commit f57be1b

Browse files
committed
work
1 parent 57dc058 commit f57be1b

File tree

5 files changed

+202
-27
lines changed

5 files changed

+202
-27
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
The short answer is: **no, they are not**:
2+
3+
The difference is that if an error happens in `f1`, then it is handled by `.catch` here:
4+
5+
```js run
6+
promise.then(f1).catch(f2);
7+
```
8+
9+
...But not here:
10+
11+
```js run
12+
promise.then(f1, f2);
13+
```
14+
15+
That's because an error/result is passed down the chain, and in the second code piece there's no chain below.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Promise then vs catch
2+
3+
Are these code fragments equal? In other words, do they behave the same way in any circumstances, for any handler functions?
4+
5+
```js
6+
promise.then(f1, f2);
7+
```
8+
9+
Versus;
10+
```js
11+
promise.then(f1).catch(f2);
12+
```
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
The answer is: **no, it won't**:
2+
3+
```js run
4+
new Promise(function(resolve, reject) {
5+
setTimeout(() => {
6+
throw new Error("Whoops!");
7+
}, 1000);
8+
}).catch(alert);
9+
```
10+
11+
As said in the chapter, there's an "implicit `try..catch`" around the function code. So all synchronous errors are handled.
12+
13+
But here the error is generated not while the executor is running, but later. So the promise can't handle it.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Error in setTimeout
2+
3+
How do you think, does the `.catch` trigger? Explain, why?
4+
5+
```js
6+
new Promise(function(resolve, reject) {
7+
setTimeout(() => {
8+
throw new Error("Whoops!");
9+
}, 1000);
10+
}).catch(alert);
11+
```

8-async/03-promise-chaining/article.md

Lines changed: 151 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -206,82 +206,206 @@ loadScript("/article/promise-chaining/one.js")
206206

207207
Once again, the `.catch` handles it.
208208

209-
**Throwing an exception is also considered an error.**
210209

211-
For instance:
210+
### Implicit try..catch
211+
212+
Throwing an exception is considered a rejection.
213+
214+
For instance, this code:
212215

213216
```js run
214217
new Promise(function(resolve, reject) {
218+
*!*
215219
throw new Error("Whoops!");
220+
*/!*
216221
}).catch(function(error) {
217222
alert(error.message); // Whoops!
218223
});
224+
```
219225

220-
## Inheriting from promise, thenables, error handling?
226+
...Works the same way as:
221227

222-
An object that has a method called `.then` is called a "thenable".
228+
```js run
229+
new Promise(function(resolve, reject) {
230+
*!*
231+
reject(new Error("Whoops!"));
232+
*/!*
233+
}).catch(function(error) {
234+
alert(error.message); // Whoops!
235+
});
236+
```
223237

224-
Instead of checking if something is `instanceof Promise`, we should usually check it for being thenable, and if it is, then treat it as a promise ("duck typing").
238+
Like there's an invisible `try..catch` around the whole code of the function, that catches errors.
225239

226-
JavaScript specification also checks the value returned by a handler for being a thenable, not exactly a promise, when it decides whether to pass it along the chain or wait for the result. So in the examples above we could use custom thenables instead of `Promise` instances.
240+
That works not only in the executor, but in handlers as well, for instance:
227241

228-
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`.
242+
```js run
243+
new Promise(function(resolve, reject) {
244+
resolve("ok")
245+
}).then(function(result) {
246+
*!*
247+
throw new Error("Whoops!");
248+
*/!*
249+
})
250+
.catch(function(error) {
251+
alert(error.message); // Whoops!
252+
});
253+
```
229254

230-
Let's
231255

256+
### Rethrowing
232257

258+
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.
233259

260+
In a regular `try..catch` we can analyze the error and maybe rethrow it can't handle. The same thing is possible for promises.
234261

262+
A handler in `.catch` can finish in two ways:
235263

264+
1. It can return a value or don't return anything. Then the execution continues "normally", the next `.then(onResolved)` handler is called.
265+
2. It can throw an error. Then the execution goes the "error" path, and the closest rejection handler is called.
236266

267+
Here is an example of the first behavior (the error is handled):
237268

269+
```js run
270+
// the execution: catch -> then
271+
new Promise(function(resolve, reject) {
238272

239-
## Error handling
273+
throw new Error("Whoops!");
240274

275+
}).catch(function(error) {
241276

277+
alert("Handled it!");
278+
*!*
279+
return "result"; // return, the execution goes the "normal way"
280+
*/!*
242281

282+
*!*
283+
}).then(alert); // result shown
284+
*/!*
285+
```
243286

287+
...And here's an example of "rethrowing":
244288

245289

246290
```js run
291+
// the execution: catch -> catch -> then
247292
new Promise(function(resolve, reject) {
248-
setTimeout(() => resolve(1), 1000);
249-
}).then(function(result) {
250293

251294
throw new Error("Whoops!");
252295

253296
}).catch(function(error) {
254297

255-
alert(error.message); // Whoops!
298+
alert("Can't handle!");
299+
*!*
300+
throw error; // throwing this or another error jumps to the next catch
301+
*/!*
302+
303+
}).catch(error => {
256304

305+
alert("Trying to handle again...");
306+
// don't return anything => execution goes the normal way
307+
308+
}).then(alert); // undefined
309+
```
310+
311+
### Unhandled rejections
312+
313+
What if we forget to handle an error?
314+
315+
Like here:
316+
317+
```js untrusted run refresh
318+
new Promise(function() {
319+
errorHappened(); // Error here (no such function)
257320
});
258321
```
259322

323+
Or here:
260324

325+
```js untrusted run refresh
326+
new Promise(function() {
327+
throw new Error("Whoops!");
328+
}).then(function() {
329+
// ...something...
330+
}).then(function() {
331+
// ...something else...
332+
}).then(function() {
333+
// ...but no catch after it!
334+
});
335+
```
261336

337+
Technically, when an error happens, the promise state becomes "rejected", and the execution should jump to the closest rejection handler. But there is none.
262338

263-
The idea is :
339+
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)):
264340

265-
- A callback in `.then` may return a result.
266341

342+
```js run
343+
// open in a new window to see in action
267344

268-
One of main purposes of promises is to make asyn
269-
The main purpose of promises
270-
Promises
345+
window.addEventListener('unhandledrejection', function(event) {
346+
alert(event.promise); // the promise that generated the error
347+
alert(event.reason); // the error itself (Whoops!)
348+
});
349+
350+
new Promise(function() {
351+
throw new Error("Whoops!");
352+
}).then(function() {
353+
// ...something...
354+
}).then(function() {
355+
// ...something else...
356+
}).then(function() {
357+
// ...but no catch after it!
358+
});
359+
```
360+
361+
In non-browser environments there's also a similar event, so we can always track unhandled errors in promises.
362+
363+
364+
An object that has a method called `.then` is called a "thenable".
365+
366+
Instead of checking if something is `instanceof Promise`, we should usually check it for being thenable, and if it is, then treat it as a promise ("duck typing").
367+
368+
JavaScript specification also checks the value returned by a handler for being a thenable, not exactly a promise, when it decides whether to pass it along the chain or wait for the result. So in the examples above we could use custom thenables instead of `Promise` instances.
369+
370+
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`.
371+
372+
## Extending promises, thenables
373+
374+
Promises are very simple by design. One of the thing they miss is the ability to cancel the process.
375+
376+
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.
377+
378+
We can inherit from `Promise` to introduce such functionality, like this:
379+
380+
381+
// TODO: NOT WORKING AS INTENDED?
382+
383+
```js run
384+
function loadScript(src) {
385+
let script = document.createElement('script');
386+
script.src = src;
387+
388+
let promise = new Promise(function(resolve, reject) {
389+
script.onload = () => resolve(script);
390+
*!*
391+
script.onerror = err => reject(new Error("Script load error: " + src)); // (*)
392+
*/!*
393+
});
394+
395+
document.head.append(script);
396+
promise.abort = () => script.remove();
397+
return promise;
398+
}
399+
400+
let promise = loadScript("/article/promise-chaining/one.js");
401+
promise.then(alert);
402+
promise.abort();
403+
```
271404

272-
Promises can be chained. That allows actions to follow one after another.
273405

274-
Here's a simple example first:
275406

276-
```js
277-
let promise = new Promise(function(resolve, reject) {
278-
setTimeout(() => resolve(""))
279-
})
280407

281408

282409

283-
What if we want to
284-
The main idea behind promises
285-
Promises can be used for asynchronous tasks that eventually finish with a result or an error.
286410

287-
We already have `loadScript`
411+
## Inheriting from promise, thenables, promise api, async/await

0 commit comments

Comments
 (0)