Skip to content

链式调用

在 JavaScript 中,Promise 的链式调用是一种非常强大且简洁的方式来处理一系列异步操作。通过链式调用,可以避免“回调地狱”,使代码更加清晰和易于维护。以下是关于 Promise 链式调用的详细介绍:

基础概念

当你在一个 Promise 对象上调用 .then() 方法时,这个方法会返回一个新的 Promise 对象。这意味着你可以在前一个 .then() 返回的 Promise 上继续调用 .then(),从而形成链式调用。每个 .then() 方法都可以接收两个可选的回调函数作为参数:第一个用于处理 Promise 成功解决(fulfilled)的情况,第二个用于处理 Promise 被拒绝(rejected)的情况。

  • then 方法必定会返回一个新的 Promise:可理解为后续处理也是一个任务

  • 新任务的状态取决于后续处理

    1. 若没有相关的后续处理,新任务的状态和前任务一致,数据为前任务的数据;
    2. 若有后续处理但还未执行,新任务挂起;
    3. 若后续处理执行了,则根据后续处理的情况确定新任务的状态:
    • 后续处理执行无错,新任务的状态为完成,数据为后续处理的返回值
    • 后续处理执行有错,新任务的状态为失败,数据为异常对象
    • 后续执行后返回的是一个任务对象,新任务的状态和数据与该任务对象一致
javascript
// 1. prom: Promise { <pending> },prom1: Promise { <pending> }
let prom = new Promise((resolve, reject) => {});
let prom1 = prom.then(() => {});
//
// 2. prom: Promise { <fulfilled>: 1 },
// prom1: Promise { <fulfilled>: 2 },
// prom2: Promise { <fulfilled>: undefined }
let prom = new Promise((resolve, reject) => {
  resolve(1);
});
let prom1 = prom.then(() => {
  return 2;
});
let prom2 = prom.then(() => {});
// 3. prom: Promise { <fulfilled>: 1 },
// prom1: Promise { <rejected>: 故意抛出的错误 },
// prom2: Promise { <fulfilled>: undefined }
let prom = new Promise((resolve, reject) => {
  resolve(1);
});
let prom1 = prom.then(() => {
  throw "故意抛出的错误";
});
let prom2 = prom.then(() => {});

基本用法

javascript
let myPromise = new Promise((resolve, reject) => {
  setTimeout(() => resolve("第一步完成"), 1000);
});

myPromise
  .then((result) => {
    console.log(result); // 输出: 第一步完成
    return "第二步完成";
  })
  .then((result) => {
    console.log(result); // 输出: 第二步完成
    return "第三步完成";
  })
  .then((result) => {
    console.log(result); // 输出: 第三步完成
  })
  .catch((error) => {
    console.error(error); // 捕获任何步骤中的错误
  });

在这个例子中,每次 .then() 方法都返回了一个新的值,这个值会被传递给链中的下一个 .then() 方法。如果在任意一个 .then() 方法中抛出了异常或者返回了一个被拒绝的 Promise,则后续的 .then() 方法将被跳过,控制流会直接进入 .catch() 方法。

错误处理

在链式调用中,.catch() 方法可以用来捕获前面任何一个 .then() 方法中发生的错误。如果你不使用 .catch(),那么未处理的错误可能会导致程序崩溃或产生不可预期的行为。

.catch(onRejected) = .then(undefined, onRejected)

javascript
myPromise
  .then((result) => {
    console.log(result);
    throw new Error("故意抛出的错误");
  })
  .then((result) => {
    console.log(result); // 不会被执行
  })
  .catch((error) => {
    console.error(error.message); // 输出: 故意抛出的错误
  });

返回新的 Promise

.then() 中不仅可以返回简单的值,还可以返回一个新的 Promise。这样,链中的下一个 .then() 将等待该 Promise 解决后再执行。

javascript
function asyncFunction() {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve("异步操作完成"), 2000);
  });
}

myPromise
  .then((result) => {
    console.log(result);
    return asyncFunction(); // 返回一个新的 Promise
  })
  .then((result) => {
    console.log(result); // 两秒后输出: 异步操作完成
  });

使用 async/await 简化链式调用

虽然 Promise 链式调用已经简化了异步代码的编写,但结合 asyncawait 可以进一步提高代码的可读性,尤其是在处理复杂的异步逻辑时。

javascript
async function run() {
  try {
    let result1 = await myPromise;
    console.log(result1);
    let result2 = await asyncFunction();
    console.log(result2);
  } catch (error) {
    console.error(error);
  }
}

run();

通过 async/await,你可以以一种看起来像是同步的方式编写异步代码,这使得代码更加直观易懂。

理解并掌握 Promise 的链式调用对于编写高效、清晰的异步 JavaScript 代码至关重要。它不仅帮助我们避免了嵌套回调带来的混乱,还提供了强大的错误处理机制。

代码题

1. 下面代码的输出结果是什么

js
const pro1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(1);
  }, 1000);
});

const pro2 = pro1.then((data) => {
  console.log(data);
  return data + 1;
});

const pro3 = pro2.then((data) => {
  console.log(data);
});

console.log(pro1, pro2, pro3);

setTimeout(() => {
  console.log(pro1, pro2, pro3);
}, 2000);

2. 下面代码的输出结果是什么

js
new Promise((resolve, reject) => {
  resolve(1);
})
  .then((res) => {
    console.log(res);
    return 2;
  })
  .catch((err) => {
    return 3;
  })
  .then((res) => {
    console.log(res);
  });

3. 下面代码的输出结果是什么

js
new Promise((resolve, reject) => {
  resolve();
})
  .then((res) => {
    console.log(res.toString());
    return 2;
  })
  .catch((err) => {
    return 3;
  })
  .then((res) => {
    console.log(res);
  });

4. 下面代码的输出结果是什么

js
new Promise((resolve, reject) => {
  throw new Error(1);
})
  .then((res) => {
    console.log(res);
    return new Error("2");
  })
  .catch((err) => {
    throw err;
    return 3;
  })
  .catch((res) => {
    console.log(res);
  });

4.2 下面代码的输出结果是什么

js
new Promise((resolve, reject) => {
  resolve(1);
})
  .then((res) => {
    console.log(res);
    return new Error("2");
  })
  .catch((err) => {
    throw err;
    return 3;
  })
  .then((res) => {
    console.log(res);
  });

5. 下面代码的输出结果是什么

js
const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject();
  }, 1000);
});
const promise2 = promise1.catch(() => {
  return 2;
});

console.log("promise1", promise1);
console.log("promise2", promise2);

setTimeout(() => {
  console.log("promise1", promise1);
  console.log("promise2", promise2);
}, 2000);

Released under the MIT License.