Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

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. Soin 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+
thrownewError("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)

AltStyle によって変換されたページ (->オリジナル) /