一、连续执行两个或者多个异步操作是一个常见的需求,在上一个操作执行成功之后,开始下一个操作,并带着上一步操作所返回的结果。
| 【示例】Promise链
1、then()函数会返回一个和原来不同的新的Promise```javascript
const promise = doSomething()
const promise2 = promise.then(successCallback, failureCallback)
或```javascript
const promise2 = doSomething().then(successCallback, failureCallback);
2、promise2不仅表示doSomething()函数的完成,也代表了你传入的successCallback或者failureCallback的完成,这两个函数也可以返回一个Promise对象,从而形成另一个异步操作,这样的话,在promise2上新增的回调函数会排在这个Promise对象的后面。 | | —- |
二、基本上,每一个Promise代表了链中另一个异步过程的完成。我们可以把回调绑定到返回的Promise上,形成一个Promise链
三、如果.then(或catch/finally都可以)处理程序(handler)返回一个 promise,那么链的其余部分将会等待,直到它状态变为 settled。当它被 settled 后,其 result(或 error)将被进一步传递下去。
1、完整的流程图:
| 【示例】返回 promise 使我们能够构建异步行为链。``` new Promise(function(resolve, reject) { setTimeout(() => resolve(1), 1000); }).then(function(result) { alert(result); // 1 return new Promise((resolve, reject) => { // () setTimeout(() => resolve(result 2), 1000); }); }).then(function(result) { // (*) alert(result); // 2 return new Promise((resolve, reject) => { setTimeout(() => resolve(result 2), 1000); }); }).then(function(result) { alert(result); // 4 });
1、这里第一个.then显示1并在(*)行返回new Promise(…)。1 秒后它会进行 resolve,然后 result(resolve的参数,在这里它是result*2)被传递给第二个.then的处理程序(handler)。<br />2、这个处理程序(handler)位于(**)行,它显示2,并执行相同的动作(action)。<br />3、所以输出与前面的示例相同:1 → 2 → 4,但是现在在每次alert调用之间会有 1 秒钟的延迟。 |
| --- |
四、确切地说,处理程序handler返回的不完全是一个 promise,而是返回的被称为 “thenable” 对象 — 一个具有方法.then的任意对象。它会被当做一个 promise 来对待。<br />1、这个想法是,第三方库可以实现自己的“promise 兼容(promise-compatible)”对象。它们可以具有扩展的方法集,但也与原生的 promise 兼容,因为它们实现了.then方法。
| 【示例】这是一个 thenable 对象的示例:
class Thenable { constructor(num) { this.num = num; } then(resolve, reject) { alert(resolve); // function() { native code } // 1 秒后使用 this.num2 进行 resolve setTimeout(() => resolve(this.num 2), 1000); // (**) } }
new Promise(resolve => resolve(1)) .then(result => { return new Thenable(result); // (*) }) .then(alert); // 1000ms 后显示 2
1、JavaScript 检查在(*)行中由.then处理程序(handler)返回的对象:如果它具有名为then的可调用方法,那么它将调用该方法并提供原生的函数resolve和reject作为参数(类似于 executor),并等待直到其中一个函数被调用。在上面的示例中,resolve(2)在 1 秒后被调用(**)。然后,result 会被进一步沿着链向下传递。 |
| --- |
2、这个特性允许我们将自定义的对象与 promise 链集成在一起,而不必继承自Promise。
<a name="pqIPr"></a>
# Catch的后续链式操作
一、有可能会在一个回调失败之后继续使用链式操作,即,使用一个catch,这对于在链式操作中抛出一个失败之后,再次进行新的操作会很有用。
| 【示例】```javascript
new Promise((resolve, reject) => {
console.log('初始化');
resolve();
})
.then(() => {
throw new Error('有哪里不对了');
console.log('执行「这个」”'); // 因为抛出了错误'有哪里不对了',所以'执行「这个」'没有被输出
})
.catch((e) => {
console.log(e);
console.log('执行「那个」');
})
.then(() => {
console.log('执行「这个」,无论前面发生了什么');
});
// 输出的结果:
初始化
Error: 有哪里不对了
执行“那个”
执行“这个”,无论前面发生了什么
| | —- |
嵌套
一、简便的Promise链式最好保持扁平化,不要嵌套Promise,因为嵌套经常会是粗心导致的。
二、在编写链式Promise时,需要注意以下示例中展示的3个错误
- 错误1:没有正确地将事物连接。
(1)当我们创建新Promise但忘记返回它时,会发生这种情况。
(2)因此,链条被打破,或者更确切地说,我们有两个独立的链条竞争(同时在执行两个异步而非一个一个地执行)。
①这意味着doFourthThing()不会等待doSomethingElse()或者doThirdThing()完成,并且将与它们并行运行,可能是无意的。
(3)单独的链也有单独的错误处理,导致未捕获的错误。
- 错误2:不必要地嵌套,实现第1个错误。
(1)嵌套限制了内部错误处理程序的范围,如果是非预期的,可能会导致未捕获的错误。
(2)其中一个变体是Promise构造函数反模式,它结合了Promise构造函数的多余使用和嵌套。
- 错误3:忘记用catch终止链
(1)这导致在大多数浏览器中不能终止的Promise链里的rejection
doSomething().then(function(result) {
doSomethingElse(result) // 没有返回 Promise 以及没有必要的嵌套 Promise
.then(newResult => doThirdThing(newResult));
}).then(() => doFourthThing());
// 最后,是没有使用 catch 终止 Promise 调用链,可能导致没有捕获的异常
1、一个好的经验法则是总是返回或终止Promise链,并且一旦你得到一个新的Promise,返回它。
| 【示例】修改后的平面化的代码``` doSomething() .then(function(result) { return doSomethingElse(result); }) .then(newResult => doThirdThing(newResult)) .then(() => doFourthThing()) .catch(error => console.log(error));
1、() => x是() => { return x}的简写 |
| --- |
2、使用async/await可以解决以上大多数问题,使用async/await时,常见的语法错误就是忘了await关键字。<br />二、嵌套Promise是一种可以限制catch语句的作用域的控制结构写法。<br />明确来说,嵌套的catch仅捕捉在其之前同时还必须是其作用域的failures,而捕捉不到在其链式以外或者其嵌套域以外的error。如果使用正确,那么可以实现高精度的错误修复。
| 【示例】
doSomethingCritical()
.then(result => doSomethingOptional()
.then(optionalResult => doSomethingExtraNice(optionalResult))
.catch(e => {
console.log(e.message)
})) // 即使有异常也会忽略,继续运行(最后会输出)
.then(() => moreCriticalStuff())
.catch(e => console.log(‘Critical failure:’ + e.message)) // 没有输出
```
1、这个内部的catch仅能捕获到doSomethingOptional()和doSomethingExtraNice()的失败,之后就恢复到moreCriticalStuff()的运行。
2、如果doSomethingCritical()失败,这个错误仅会被最后的(外部)catch语句捕获到。 |
| —- |