1. 第 79 天:学习进入关键转折点
如果你也正在自学的路上,你应该能体会那种感觉——在坚持了两三个月之后,学习状态会出现一次明显的分层。前三十天靠热情,中间三十天靠习惯,而走到第七十多天的时候,能不能突破"什么都会一点、什么都不精"的瓶颈,就看这个阶段的选题和心态了。
我给自己定的方向是前端开发,从 HTML、CSS 起步,走到第 79 天时,JavaScript 已经学完基础语法、DOM 操作、ES6 常用特性,也做了几个静态页面和一个小型 Todo 应用。但说实话,写出来的代码都是"串行思维"——等一个请求回来,再发下一个,页面卡顿、请求排队、状态混乱的问题时有发生。说到底,是因为一直绕开了 JavaScript 最核心也最抽象的部分:异步机制。
所以第 79 天我没有急着继续向下推进新框架,而是把这一天整块留给"异步编程"这个主题。计划分三块:先把事件循环的机制彻底理清,然后用一个实际的小项目把 Promise 用熟,最后复盘 79 天以来的学习路线,看看接下来的方向怎么调整。这篇文章就是当天学习过程的完整记录,原始笔记比较零散,我整理成了由浅入深的结构化内容,把踩过的坑和推导过程也一并写清楚,希望能给同样卡在异步这里的人一点参考。
这一天的计划其实并不复杂,但安排上有个讲究:上午只做输入,下午只做输出。上午看资料、画执行流程图、抄示例代码,下午完全不看新知识,只把手头的"并发请求调度器"用 Promise 从零实现一遍,遇到问题先回忆原理再查资料。这种"输入和输出隔离"的做法,是我从第 60 天开始坚持的,也是今天能把这几个知识点真正内化的关键。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 重头戏:事件循环到底是怎么回事
2.1 先从"为什么需要异步"说起
JavaScript 是单线程语言,这意味着同一时刻只能做一件事。如果所有操作都排队执行,遇到网络请求、文件读取这样的耗时任务,整个页面就会卡死,用户点了按钮半天没反应。为了解决这个问题,JavaScript 设计出一套"先干完能干的,再回过头处理耗时任务"的调度机制,这就是事件循环。
理解事件循环,最关键的是分清两套队列:宏任务队列(macrotask queue)和微任务队列(microtask queue)。很多初学者有一个误区,认为异步任务就是"扔到一边,等有结果了再拿回来执行"。这个说法方向对了,但细节不对——异步任务并不是"有了结果再执行",而是"到点了就执行"。比如 setTimeout 设置为 0 毫秒,并不是立即执行,而是至少等待当前同步代码跑完,再排到宏任务队列里。
我整理了一下自己终于看明白事件循环时的笔记,核心就四句话:
- 同步代码永远最先执行,执行完才轮到队列里的任务。
- 每个宏任务执行完之后,必须把当前微任务队列里的所有任务全部清空,才能执行下一个宏任务。
- 微任务包括 Promise 的 then/catch/finally、queueMicrotask、MutationObserver。宏任务包括 setTimeout、setInterval、I/O 操作、UI 渲染。
- 事件循环不是"两个队列交替执行",而是"宏任务一个一个执行,微任务一堆一堆清空"。
这四句话看起来简单,但真正遇到复杂嵌套的异步代码时,很多人还是会栽跟头。
2.2 用一段代码验证自己的理解
我建议所有学到这里的人,不要只看资料,一定要亲手动笔画出执行顺序。当天我在纸上推演了下面这段代码,第一次推演结果全错,后来才逐步纠正了理解:
javascript复制console.log('A');
setTimeout(() => {
console.log('B');
}, 0);
Promise.resolve()
.then(() => {
console.log('C');
return Promise.resolve();
})
.then(() => {
console.log('D');
});
queueMicrotask(() => {
console.log('E');
});
console.log('F');
我在纸上先写了一遍答案,当时写的是 A、F、B、C、D、E,原因是想当然地认为 queueMicrotask 和 Promise 是两类东西。实际跑出来的结果是 A、F、C、E、D、B。这里的关键点有两个:
第一个关键点,Promise 的 then 回调注册时机。Promise.resolve() 执行的时候,状态已经变为 fulfilled,then 里的回调会立刻被放进微任务队列,而不是等下一轮。所以 C 和 E 都在当前同步代码 F 之后、第一个宏任务 B 之前执行。
第二个关键点,为什么 D 在 E 后面?这涉及到 then 链的一个隐藏行为:当第一个 then 的回调返回一个新的 Promise 时,这个新
