第三十六天:Promise — 告别回调地狱
蜗牛往上爬 · 前端工业可视化学习笔记 第 36 篇
异步是 JS 的灵魂,也是大屏的命脉——所有数据都来自接口请求。今天从回调地狱的痛点出发,理解 Promise 的三种状态、then/catch/finally 的用法,用链式调用把"金字塔嵌套"拍扁。
目录
一、为什么需要异步
JS 是单线程语言——同一时间只能做一件事。耗时操作(网络请求、定时器)如果同步等待,页面就卡死了。
console.log("1:开始请求");
setTimeout(() => console.log("2:定时器回调"), 1000);
console.log("3:同步代码");
// 输出:1 → 3 → 2(先跑完同步,再执行回调)
机制:耗时操作交给浏览器其他线程,完成后把回调放入"任务队列",JS 主线程空闲时再执行。
二、回调地狱
多个有依赖的异步操作(先登录拿 token → 再请求列表 → 再请求详情),ES5 只能层层嵌套:
login(user, (err, token) => {
if (err) return handleError(err);
getDevices(token, (err, devices) => {
if (err) return handleError(err);
getDetail(devices[0].id, (err, detail) => {
if (err) return handleError(err);
render(detail); // ⚠ 四层嵌套,向右无限膨胀
});
});
});
痛点:嵌套深、可读性差、错误处理重复、难以复用。
三、Promise:异步的容器
Promise 保存着"未来才会有"的值,三种状态:
pending(等待)
├── resolve(value) → fulfilled(成功)
└── reject(error) → rejected(失败)
状态一旦改变,不可逆。
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
const success = true;
if (success) {
resolve({ id: "CNC-001", temp: 65 }); // 成功
} else {
reject(new Error("网络错误")); // 失败
}
}, 1000);
});
promise
.then(data => console.log("成功:", data))
.catch(err => console.log("失败:", err.message));
封装模拟接口(练习用):
// 模拟请求设备数据(60% 成功率)
function fetchDevices(delay = 1000) {
return new Promise((resolve, reject) => {
setTimeout(() => {
Math.random() > 0.4
? resolve([{ id: "CNC-001", temp: 65 }])
: reject(new Error("网络超时"));
}, delay);
});
}
四、then / catch / finally
fetchDevices()
.then(devices => {
console.log("渲染列表", devices);
return devices.length; // ⭐ 返回值传给下一个 then
})
.then(count => console.log(`共 ${count} 台`))
.catch(err => {
// 捕获链条中任何一环的错误
console.error("错误:", err.message);
})
.finally(() => {
// 无论成败都执行(关 loading)
console.log("请求结束");
});
五、链式调用拍扁嵌套
// 封装成 Promise 的三个接口
const login = user =>
new Promise(resolve => setTimeout(() => resolve({ token: "abc" }), 500));
const getDevices = token =>
new Promise(resolve => setTimeout(() => resolve([{ id: "CNC-001" }]), 500));
const getDetail = id =>
new Promise(resolve => setTimeout(() => resolve({ id, temp: 65 }), 500));
// 链式调用(扁平!)
login("admin")
.then(({ token }) => getDevices(token))
.then(devices => getDetail(devices[0].id))
.then(detail => console.log("详情:", detail))
.catch(err => console.error(err.message)) // ⭐ 一处捕获全部
.finally(() => console.log("流程结束"));
对比回调版:嵌套变扁平、错误统一处理、每步可独立复用。
链式核心规则:
then 回调返回普通值 → 直接传给下一个 then
then 回调返回 Promise → 下一个 then 会等它完成
中途 throw → 跳过后面的 then,直达 catch
catch 之后可以继续 then(链条恢复)
六、宏任务与微任务
console.log("1:同步");
setTimeout(() => console.log("2:宏任务"), 0);
Promise.resolve().then(() => console.log("3:微任务"));
console.log("4:同步");
// 输出:1 → 4 → 3 → 2
// ⭐ 微任务(Promise.then)比宏任务(setTimeout)先执行
事件循环规则:同步代码 → 清空全部微任务 → 取一个宏任务 → 再清微任务 → ……
七、总结
🎯 Promise 解决了"嵌套",明天学并发控制(Promise.all 四剑客)和 async/await——异步的终极形态。