一、同步与异步的本质区别
在 JavaScript 中,代码执行模式分为同步(Synchronous)和异步(Asynchronous)两种。理解它们的区别是掌握 Promise 的基础。
同步执行:按部就班的流水线
同步代码按照书写顺序依次执行,前一个任务未完成时,后续任务会被阻塞。这就像工厂里的流水线,每个环节都必须等待上一步完成才能开始。
console.log("任务1:准备原材料");
console.log("任务2:加工零件");
console.log("任务3:组装产品");
// 输出顺序:任务1 → 任务2 → 任务3
异步执行:高效的并行协作
异步代码在执行耗时操作(如网络请求、定时器)时不会阻塞主线程,而是继续执行后续代码。
console.log("开始下载文件");
setTimeout(() => {
console.log("文件下载完成");
}, 2000);
console.log("继续执行其他任务");
// 输出顺序:开始下载 → 继续执行 → 2秒后下载完成
二、Promise 的诞生:解决异步编程的痛点
早期的异步编程主要依赖回调函数,但多层嵌套的回调会导致代码可读性变差,形成所谓的 "回调地狱"(Callback Hell)。
回调地狱示例
fetchUserData(userId, (userData) => {
fetchUserPosts(userData.id, (posts) => {
fetchPostComments(posts[0].id, (comments) => {
// 嵌套层级越深,代码越难维护
saveToCache(comments, (success) => {
if (success) console.log("缓存成功");
});
});
});
});
Promise 的解决方案
Promise 是一种专门用于处理异步操作的对象,它代表一个异步操作的最终完成(或失败)及其结果值。Promise 有三种状态:
pending(进行中)
fulfilled(已成功)
rejected(已失败)
状态一旦改变,就会永久保持该状态,不会再发生变化。这种特性使得 Promise 可以有效避免回调地狱。
三、Promise 的基本用法
创建 Promise
const promise = new Promise((resolve, reject) => {
// 模拟异步操作(如网络请求、文件读取)
setTimeout(() => {
const success = true; // 假设操作成功
if (success) {
resolve("操作成功"); // 操作成功时调用,传递结果
} else {
reject("操作失败"); // 操作失败时调用,传递错误信息
}
}, 1000);
});
消费 Promise
promise
.then((result) => {
console.log(result); // 输出:操作成功
})
.catch((error) => {
console.error(error); // 操作失败时执行
})
.finally(() => {
console.log("无论成功或失败都会执行");
});
四、Promise 链式调用:优雅处理异步流程
Promise 的强大之处在于可以链式调用,避免多层嵌套。每个 then 方法都会返回一个新的 Promise,允许继续调用下一个 then。
链式调用示例
function xq() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('相亲成功');
resolve() // 成功状态
}, 1000)
})
}
function marry() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('结婚了');
resolve()
}, 2000)
})
}
function baby() {
setTimeout(() => {
console.log('小孩出生');
}, 500)
}
// 1. 执行 xq 函数,立即返回一个promise 实例对象,但是此时改对象的状态是 pending (等待状态)
// 2. .then立即触发,但是 then 里面的回调函数没有触发
// 3. 等待 xq 函数里面的reslove()执行完毕,此时实例对象的状态会变更为 fulfilled (成功状态),此时 .then 里面的回调函数会触发执行
xq() // 里面执行到了 reslove()
.then(() => { // then的源码里面也返回了一个 promise 实例对象,状态默认继承自 xq 函数返回的对象的状态
return marry()
})
.then(() => { // 保正第一个 then 返回的对象状态继承于marry 函数返回的对象状态
baby()
})
五、async/await:Promise 的语法糖
ES2017 引入的 async/await 是基于 Promise 的语法糖,使异步代码看起来更像传统的同步代码,提高了可读性。
async/await 示例
// 模拟异步操作(延迟返回数据)
function fetchData() {
return new Promise(resolve => {
setTimeout(() => resolve("数据加载完成"), 1000);
});
}
function processData(data) {
return new Promise(resolve => {
setTimeout(() => resolve(data + " -> 处理完成"), 800);
});
}
// 异步函数示例
async function main() {
try {
console.log("开始执行...");
// 顺序执行异步操作
const data = await fetchData();
console.log("第一步:", data);
const result = await processData(data);
console.log("第二步:", result);
} catch (error) {
console.error("出错了:", error.message);
}
}
// 执行主函数
main();
六、总结
Promise 是 JavaScript 异步编程的核心概念,它解决了回调地狱问题,提供了优雅的异步流程控制方式。通过 async/await,异步代码可以写得像同步代码一样清晰。
同步:按顺序执行,阻塞主线程。
异步:不阻塞主线程,通过回调、Promise 等处理结果。
Promise 状态:pending → fulfilled/rejected(单向不可逆)。
链式调用:通过 .then() 串联多个异步操作。
错误处理:使用 .catch() 或 try/catch 捕获异步错误。