静下心来的个人专栏
上一篇

链式Promise——返回Promise

广告
选中文字可对指定文章内容进行评论啦,→和←可快速切换按钮,绿色背景文字可以点击查看评论额。
大纲

Promise链返回Promise

从 Promise 处理程序返回原始值允许在 Promise 之间传递数据,但是如果您返回一个对象呢?如果对象是一个promise,那么需要采取额外的步骤来确定如何执行。 考虑以下示例:

const promise1 = Promise.resolve(42);
const promise2 = Promise.resolve(43);

promise1.then(value => {
  console.log(value); // 42
  return promise2;
}).then(value => {
  console.log(value); // 43
});

在这段代码中,promise1解析为 42。promise1 的fullfilment handler返回 p2,这是一个已经处于resolved的promise。接着马上调用第二个fullfilment handler,因为 promise2 已经resolved。 如果 promise2 被reject,将调用rejection handler(如果存在)而不是第二个fullfilment handler.

关于这种模式需要认识到的重要一点是,第二个fullfilment handler没有添加到 promise2,而是添加到第三个 promise,使得前面的示例等价于:

const promise1 = Promise.resolve(42);
const promise2 = Promise.resolve(43);

const promise3 = promise1.then(value => {
  console.log(value); // 42
  return promise2;
});
promise3.then(value => {
  console.log(value); // 43
});

在这里,很明显,第二个fullfilment handler附加到 promise3 而不是 promise2。 这是一个微妙但重要的区别,因为如果 promise2 被reject,则不会调用第二个fullfilment handler。 例如:

const promise1 = Promise.resolve(42);
const promise2 = Promise.reject(43);

promise1.then(value => {
  console.log(value); // 42
  return promise2;
}).then(value => {
  console.log(value); // never called
});

在此示例中,第二个fullfilment handler永远不会被调用,因为 promise2 被拒绝。 但是,您可以附加一个rejection handler:

const promise1 = Promise.resolve(42);
const promise2 = Promise.reject(43);

promise1.then(value => {
  console.log(value); // 42
  return promise2;
}).catch(value => {
  console.error(value); // 43
});

在这里,由于 promise2 被reject,rejection handler被调用。 来自 promise2 的拒绝值 43 被传递到该rejection handler。

 

当一个操作需要多个 Promise 才能执行完成时,从fullfilment handler返回一个 Promise 很有帮助。例如, fetch() 需要第二个 promise 来读取响应的正文。 要读取 JSON 正文,您需要使用 response.json(),它返回另一个promise。 这是不使用 Promise 链的样子:

const promise1 = fetch("books.json");

promise1.then(response => {

  promise2 = response.json();
  promise2.then(payload => {
    console.log(payload);
  }).catch(reason => {
    console.error(reason.message);
  });
}).catch(reason => {
  console.error(reason.message);
});

此代码需要两个不同的rejection handler来捕获流程的两个不同步骤中的潜在错误。 从第一个fullfilment handler返回第二个promise简化了代码:

const promise = fetch("books.json");

promise.then(response => {
  return response.json();
}).then(payload => {
 
  console.log(payload);
}).catch(reason => {
  console.error(reason.message);
});

在这里,当接收到响应时调用第一个fullfilment handler,然后返回一个以 JSON 格式读取响应正文的promise。 当正文已被读取并且有效负载已准备好使用时,将调用第二个fullfilment handler。 您只需要在 Promise 链的末端有一个rejection handler即可捕获沿途发生的错误。

 

特殊情况finally

使用 finally() 从fullfilment handler返回一个promise也表现出与使用 then() 或 catch() 不同的行为。 首先,如果您从fullfilment handler返回一个resolved 的promise,那么该promise将被忽略以支持来自原始promise的值,如下例所示:

const promise = Promise.resolve(42);

promise.finally(() => {
  return Promise.resolve(44);
}).then(value => {
  console.log(value); // 42
});

在此示例中,fullfilment handler返回一个以 44 实现的 Promise,但返回的 Promise 以原始 Promise 的值 42处理。

但是,如果您从fullfilment handler返回一个rejected的 Promise,则返回的 Promise 采用该原因并且返回的 Promise rejected,如下所示:

const promise = Promise.resolve(42);

promise.finally(() => {
  return Promise.reject(43);
}).catch(reason => {
  console.error(reason); // 43
});

即使原始promise被拒绝也是如此,如下例所示:

const promise = Promise.reject(43);

promise.finally(() => {
  return Promise.reject(45);
}).catch(reason => {
  console.log(reason); // 45
});

从fullfilment handler返回被拒绝的 Promise 在功能上等同于抛出错误:返回的 Promise 以指定的原因被拒绝。

 

promise链返回promise,延迟执行

当 promise executor 被执行时,从fullfilment 或rejection handler返回的 promises 不会改变。 第一个定义的 Promise 将首先运行它的 executor; 然后第二个 Promise executor 将运行,依此类推。 返回 Promise 只允许您定义对 Promise 结果的额外响应。 您可以通过在fullfilment handler中创建新的promise来推迟fullfilment handler的执行。 例如:

const p1 = Promise.resolve(42);
p1.then(value => {
  console.log(value); // 42
  // create a new promise
  const p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(43);
    }, 500);
  });
  return p2;
}).then(value => {
  console.log(value); // 43
});

在此示例中,在 p1 的fullfilment handler中创建了一个新的promise。 这意味着在 p2 完成之前,第二个fullfilment handler不会执行。 p2 的执行者被延迟使用 setTimeout() 可以缩短 500 毫秒,但更现实是,您可能会发出网络或文件系统请求。 当您想等到先前的promise完成后再开始新的异步操作时,此模式很有用。

版权声明:著作权归作者所有。

X

欢迎加群学习交流

联系我们