某天,JavaScript世界突然陷入混乱:
console.log("我要先执行!");
setTimeout(() => console.log("不,我才是主角!"), 0);
console.log("你们往后稍稍");
结果输出顺序让程序员们集体崩溃——这简直是代码界的"插队"现场!今天,就让我们用Promise和Async/Await这两把金钥匙,整治这个混乱的异步江湖。
🧠 第一章:CPU的"时间管理术"
在1.html中上演的"同步异步大乱斗"揭示了CPU的运作秘密:
<script>
setTimeout(() => console.log('异步任务:2222'), 10); // 先注册一个定时器,但不执行
// 同步任务立即抢占CPU
for (let i = 0; i < 100; i++) {
console.log('同步轰炸:2222');
}
console.log('1111'); // 最先输出!
</script>
CPU的"渣男行为准则":
- 进程是后宫(资源分配的最小单元)
- 线程是嫔妃(执行代码的最小单元)
- 轮询机制是翻牌子(每秒60次的"临幸"速度)
- 同步任务是正宫(立即执行不拖延)
- 异步任务是答应(先登记,等通知)
就像动画片每秒24帧,CPU也在不断切换任务。当遇到setTimeout这种"拖延症患者",CPU会说:"你先挂号,我忙完手头100个循环再说!"
第二章同步/异步任务的相互争宠
同步 前一个任务结束后再执行后一个任务,程序的执行顺序与任务的排列顺序是一致的、同步的。比如做饭的同 步做法:我们要烧水煮饭,等水开了(10分钟之后),再去切菜,炒菜。
异步 你在做一件事情时,因为这件事情会花费很长时间,在做这件事的同时,你还可以去处理其他事 情。比如做饭的异步做法,我们在烧水的同时,利用这10分钟,去切菜,炒菜。 他们的本质区别: 这条流水线上各个流程的执行顺序不同。
同步任务
同步任务都在主线程上执行,形成一个执行栈。
异步任务
JS 的异步是通过回调函数实现的。 一般而言,异步任务有以下三种类型: 1、普通事件,如 click、resize 等
2、资源加载,如 load、error 等
3、定时器,包括 setInterval、setTimeout 等 异步任务相关添加到任务队列中(任务队列也称为消息队列)。
- 先执行执行栈中的同步任务。
- 异步任务放入任务队列中。
- 一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取任务队列中的异步任务,于是被读取的异步任务结束等待 状态,进入执行栈,开始执行。
🤝 第三章:回调地狱的"救世主"
Promise的"画饼大法"
const readFilePromise = new Promise((resolve) => {
fs.readFile('./1.html', (err, data) => {
console.log("读到内容啦!");// 只有执行完读取的异步任务,才能执行 console.log('吃饼时间到!')
resolve(); // 举手示意:饼烤好了!
})
});
readFilePromise.then(() => {
console.log('吃饼时间到!'); // 优雅地吃饼
});
Promise的三大绝招:
new Promise():开饼铺(创建承诺)resolve():摇铃铛(通知饼已出炉)then():领饼处(兑现承诺)
这就好比去网红餐厅排队:"您前面还有83桌,扫码通知您~"
🔮 第四章:Promise的"读心术"
在2.html中,Promise展示了真正的"预知未来":
html
复制
下载
运行
<script>
const p = new Promise((resolve) => {
console.log('【同步】立即执行'); // 第1步
setTimeout(() => {
console.log('【异步】延迟任务'); // 第3步
resolve(); // 发信号弹
}, 10);
});
p.then(() => {
console.log('【回调】未来已来'); // 第4步
});
console.log('【同步】继续执行'); // 第2步
</script>
执行顺序的"时空法则"
- 同步代码永远"插队"优先
new Promise()是同步的"传令官"then()是异步的"接盘侠"resolve()是穿越时空的"门钥匙"
类比快递柜:
new Promise()是下单(同步),resolve()是放入快递柜(异步完成),then()是取件(处理结果)
✨ 第四章:Async/Await——异步界的"美图秀秀"
我们来看我们怎么写网络请求:
document.addEventListener('DOMContentLoaded', async () => {
console.log('DOMContentLoaded');
DOM 已经加载完毕了 可以尽快发送请求
await 等到右边的异步任务执行完, 异步变同步
fetch('https://api.github.com/users/dwk-lzd/repos')
.then(res => res.json())
.then(data => {
console.log(data);
document.getElementById('repos').innerHTML = data.map(item => {
return `
<li><a href=${item.html_url}>${item.full_name}</a></li>
`
}).join('')
})
这样是不是看着不优雅,不不整洁,没事,我们es6推出了控制流程的新套路async
在3.html和4.html中,我们看到魔法诞生:
<!-- 3.html 网络请求变优雅 -->
<script>
document.addEventListener('DOMContentLoaded', async () => {
const res = await fetch('https://api.github.com/repos'); // 等请求
const data = await res.json(); // 等解析
// 同步写法写异步!
document.getElementById('repos').innerHTML = data.map(item => `
<li><a href=${item.url}>${item.name}</a></li>
`).join('');
});
</script>
<!-- 4.html 定时器也能同步化 -->
<script>
(async function() {
const p = new Promise(resolve => {
setTimeout(() => resolve('success'), 1000);
});
const res = await p; // 优雅地等待
console.log(res); // 1秒后输出success
console.log('1111'); // 接着输出
})();
</script>
更加语义化,更加通俗易懂
Async/Await的"修仙口诀"
- async 修饰函数:"本仙要闭关修炼异步大法"
- await 等待结果:"等本仙出关再继续"
就像泡面流程:
async是烧水(准备),await是等3分钟(暂停),开吃是后续代码(继续执行)
🧩 终极奥义:异步编程的"进化树"
回调地狱
Promise链
Async/Await
同步写法写异步
各阶段特点对比:
| 时代 | 代表 | 痛点 | 解决方案 |
|---|---|---|---|
| 原始社会 | setTimeout(callback) | 回调金字塔 | 无解 |
| 工业革命 | .then().then() | 链式冗长 | Promise链 |
| 信息时代 | async/await | 需要错误处理 | try/catch |
| 元宇宙 | 顶层await | 模块加载 | ES2022新特性 |
💡 总结:异步编程的"三大定律"
- 同步优先律:只要不是异步,CPU永远"见缝插针"
- 承诺兑现律:Promise不消灭异步,而是管理异步
- 等待守恒律:Async/Await只是语法糖,本质还是Promise
记住这个修仙口诀:
"异步本是无情物,
Promise给它上规矩,
Async/Await化同步,
从此代码似天书!"