|
| 1 | +Promise 进阶 |
| 2 | +======== |
| 3 | + |
| 4 | +我们继续学习 Promise。 |
| 5 | + |
| 6 | +## 错误处理 |
| 7 | + |
| 8 | +Promise 会自动捕获内部异常,并交给 `rejected` 响应函数处理。比如下面这段代码: |
| 9 | + |
| 10 | +```javascript |
| 11 | +new Promise( resolve => { |
| 12 | + setTimeout( () => { |
| 13 | + throw new Error('bye'); |
| 14 | + }, 2000); |
| 15 | +}) |
| 16 | + .then( value => { |
| 17 | + console.log( value + ' world'); |
| 18 | + }) |
| 19 | + .catch( error => { |
| 20 | + console.log( 'Error: ', error.message); |
| 21 | + |
| 22 | +// 输出: |
| 23 | +// (2秒后)Error: bye |
| 24 | +// at Timeout.setTimeout [as _onTimeout] (/path/to/error.js:7:11) |
| 25 | +// at ontimeout (timers.js:488:11) |
| 26 | +// at tryOnTimeout (timers.js:323:5) |
| 27 | +// at Timer.listOnTimeout (timers.js:283:5) |
| 28 | +``` |
| 29 | +
|
| 30 | +可以看到,2秒之后,因为在 Promise 的执行器里抛出了错误,所以跳过了 `.then()`,进入 `.catch()` 处理异常。 |
| 31 | +
|
| 32 | +正如我们前面所说,`.then(fulfilled, reject)` 其实接收两个参数,分别作为成功与失败的回调。不过在实践中,我更推荐上面的做法,即不传入第二个参数,而是把它放在后面的 `.catch()` 里面。这样有两个好处: |
| 33 | +
|
| 34 | +1. 更加清晰,更加好读 |
| 35 | +2. 可以捕获前面所有 `.then()` 的错误,而不仅是这一步的错误 |
| 36 | +
|
| 37 | +> 在小程序里需要注意,抛出的错误会被全局捕获,而 `.catch` 反而不执行,所以该用两个参数还是要用。 |
| 38 | +
|
| 39 | +## 更复杂的情况 |
| 40 | +
|
| 41 | +当队列很长的时候,情况又如何呢?我们看一段代码: |
| 42 | +
|
| 43 | +```javascript |
| 44 | +new Promise(resolve => { |
| 45 | + setTimeout(() => { |
| 46 | + resolve(); |
| 47 | + }, 1000); |
| 48 | +}) |
| 49 | + .then( () => { |
| 50 | + console.log('start'); |
| 51 | + throw new Error('test error'); |
| 52 | + }) |
| 53 | + .catch( err => { |
| 54 | + console.log('I catch: ', err); |
| 55 | + |
| 56 | + // 下面这一行的注释将引发不同的走向 |
| 57 | + // throw new Error('another error'); |
| 58 | + }) |
| 59 | + .then( () => { |
| 60 | + console.log('arrive here'); |
| 61 | + }) |
| 62 | + .then( () => { |
| 63 | + console.log('... and here'); |
| 64 | + }) |
| 65 | + .catch( err => { |
| 66 | + console.log('No, I catch: ', err); |
| 67 | + }); |
| 68 | + |
| 69 | +// 输出: |
| 70 | +// start |
| 71 | +// I catch: test err |
| 72 | +// arrive here |
| 73 | +// ... and here |
| 74 | +``` |
| 75 | +
|
| 76 | +实际上,`.catch()` 仍然会使用 `Promise.resolve()` 返回其中的响应函数的执行结果,与 `.then()` 并无不同。所以 `.catch()` 之后的 `.then()` 仍然会执行,如果想彻底跳出执行,就必须继续抛出错误,比如把上面代码中的 `another error` 那行注释掉。这也需要大家注意。 |
| 77 | +
|
| 78 | +## 总结 |
| 79 | +
|
| 80 | +简单总结一下 Promise 的错误处理。与异步回调相比,它的作用略强,可以抛出和捕获,基本可以按照预期的状态执行。然而它仍然不是真正的 `try/catch/throw`,在队列很长的时候,捕获错误也很容易出错,所以还要小心。 |
| 81 | +
|
| 82 | +另外,所有执行器和响应函数里的错误都不会真正进入全局环境,所以我们有必要在所有队列的最后一步增加一个 `.catch()`,防止遗漏错误造成意想不到的问题。 |
| 83 | +
|
| 84 | +```javascript |
| 85 | +doSomething() |
| 86 | + .doAnotherThing() |
| 87 | + .doMoreThing() |
| 88 | + .catch( err => { |
| 89 | + console.log(err); |
| 90 | + }); |
| 91 | +``` |
| 92 | +
|
| 93 | +在 Node.js7 之后,没有捕获的 Promise 错误会触发一个 Warning,虽然不是很强,但也足够大家发现错误了。 |
0 commit comments