链式调用
在 JavaScript 中,Promise 的链式调用是一种非常强大且简洁的方式来处理一系列异步操作。通过链式调用,可以避免“回调地狱”,使代码更加清晰和易于维护。以下是关于 Promise 链式调用的详细介绍:
基础概念
当你在一个 Promise 对象上调用 .then() 方法时,这个方法会返回一个新的 Promise 对象。这意味着你可以在前一个 .then() 返回的 Promise 上继续调用 .then(),从而形成链式调用。每个 .then() 方法都可以接收两个可选的回调函数作为参数:第一个用于处理 Promise 成功解决(fulfilled)的情况,第二个用于处理 Promise 被拒绝(rejected)的情况。
then 方法必定会返回一个新的 Promise:可理解为后续处理也是一个任务
新任务的状态取决于后续处理:
- 若没有相关的后续处理,新任务的状态和前任务一致,数据为前任务的数据;
- 若有后续处理但还未执行,新任务挂起;
- 若后续处理执行了,则根据后续处理的情况确定新任务的状态:
- 后续处理执行无错,新任务的状态为完成,数据为后续处理的返回值
- 后续处理执行有错,新任务的状态为失败,数据为异常对象
- 后续执行后返回的是一个任务对象,新任务的状态和数据与该任务对象一致
// 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(() => {});基本用法
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)
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 解决后再执行。
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 链式调用已经简化了异步代码的编写,但结合 async 和 await 可以进一步提高代码的可读性,尤其是在处理复杂的异步逻辑时。
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. 下面代码的输出结果是什么
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. 下面代码的输出结果是什么
new Promise((resolve, reject) => {
resolve(1);
})
.then((res) => {
console.log(res);
return 2;
})
.catch((err) => {
return 3;
})
.then((res) => {
console.log(res);
});3. 下面代码的输出结果是什么
new Promise((resolve, reject) => {
resolve();
})
.then((res) => {
console.log(res.toString());
return 2;
})
.catch((err) => {
return 3;
})
.then((res) => {
console.log(res);
});4. 下面代码的输出结果是什么
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 下面代码的输出结果是什么
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. 下面代码的输出结果是什么
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);