小葫芦姥姥 發表於 2025-6-23 09:47:00

Web前端入门第 68 问:JavaScript 事件循环机制中的微任务与宏任务

<p><strong>JS 是单线程语言。这句话对不对?</strong></p>
<p>按照目前的情况来看,JS 自从支持了 <code>Web Worker</code> 之后,就不再是单线程语言了,但 Worker 的工作线程与主线程有区别,在 Worker 的工作线程中无法直接操作 DOM、window 对象或大多数浏览器 API(如 localStorage),Worker 的全局对象也不再是 window 对象,而是 self。</p>
<p>Worker 中的事件循环与主线程相互独立,互不影响,但执行顺序还是得遵循 JS 的语法规则。</p>
<h2 id="宏任务">宏任务</h2>
<p>宏任务表示执行时间较长的任务,在每次时间循环时只会执行一个宏任务,执行完毕后处理微任务队列,所有微任务都执行完毕后进入下一个宏任务。</p>
<p>JS 常见宏任务类型:</p>
<ol>
<li>定时器任务:setTimeout / setInterval</li>
<li>DOM 事件回调(如 click、scroll)</li>
<li>I/O 操作(如文件读取、网络请求)</li>
<li>浏览器用于执行动画的方法 <code>requestAnimationFrame</code> ,执行时机与渲染相关</li>
<li>Node.js 环境的 <code>setImmediate</code></li>
<li>script 标签内主线程的同步代码(整体作为一个宏任务)</li>
</ol>
<h2 id="微任务">微任务</h2>
<p>微任务表示更轻量的异步任务,当宏任务执行完毕之后立即执行。</p>
<p>JS 常见微任务类型:</p>
<ol>
<li><code>Promise.then()</code> / <code>Promise.catch()</code> / <code>Promise.finally()</code></li>
<li>浏览器监听 DOM 变化的 API 对象,比如:<code>MutationObserver</code></li>
<li>手动添加微任务API方法:<code>queueMicrotask()</code></li>
<li>nodejs 中的 <code>process.nextTick()</code></li>
</ol>
<h2 id="代码解析">代码解析</h2>
<p>看这么一段代码:</p>
<pre><code class="language-js">(function() {
console.log(1)
setTimeout(() =&gt; { console.log(2); });
queueMicrotask(() =&gt; console.log(3))
new Promise(resolve =&gt; {
    console.log(4);
    setTimeout(() =&gt; {
      resolve();
      console.log(5);
    }, 0);
    Promise.resolve().then(() =&gt; console.log(6));
    console.log(7);
}).then(() =&gt; {
    console.log(8);
    Promise.resolve().then(() =&gt; console.log(9));
});
console.log(10);
})();
</code></pre>
<p>分析代码:</p>
<pre><code class="language-js">(function() {
console.log(1) // 同步任务
setTimeout(() =&gt; { console.log(2); });
queueMicrotask(() =&gt; console.log(3))
new Promise(resolve =&gt; {
    console.log(4); // 同步任务
    setTimeout(() =&gt; { // 宏任务
      resolve(); // 宏任务的同步任务
      console.log(5); // 宏任务中的同步任务
    }, 0);
    Promise.resolve().then(() =&gt; console.log(6)); // 微任务
    console.log(7); // 同步任务
}).then(() =&gt; { // 微任务
    console.log(8); // 微任务中的同步任务
    Promise.resolve().then(() =&gt; console.log(9)); // 微任务中的微任务
});
console.log(10); // 同步任务
})();
</code></pre>
<h3 id="第一轮">第一轮</h3>
<p>首先同步代码的宏任务优先级最高,不管微任务还是宏任务,同步代码都会先执行。</p>
<p>所以上面代码会优先执行:</p>
<pre><code class="language-js">console.log(1)
console.log(4);
console.log(7);
console.log(10);
</code></pre>
<p>接着开始处理微任务:</p>
<pre><code class="language-js">queueMicrotask(() =&gt; console.log(3))
Promise.resolve().then(() =&gt; console.log(6));
</code></pre>
<p>微任务处理完,开始执行下一轮宏任务。</p>
<h3 id="第二轮">第二轮</h3>
<p>这一轮中的宏任务只有一个 setTimeout,执行完之后由于没有微任务队列,所以直接执行下一轮宏任务。</p>
<pre><code class="language-js">setTimeout(() =&gt; { console.log(2); });
</code></pre>
<h3 id="第三轮">第三轮</h3>
<p>这一轮的宏任务中有同步代码。</p>
<pre><code class="language-js">setTimeout(() =&gt; { // 宏任务
resolve(); // 宏任务的同步任务
console.log(5); // 宏任务中的同步任务
}, 0);
</code></pre>
<p>在执行完 <code>resolve()</code> 之后,会将 Promise.then 的回调函数放入微任务队列中,所以在宏任务执行完之后会开始微任务:</p>
<pre><code class="language-js">then(() =&gt; { // 微任务
console.log(8); // 微任务中的同步任务
Promise.resolve().then(() =&gt; console.log(9)); // 微任务中的微任务
})
</code></pre>
<h3 id="最终的打印顺序">最终的打印顺序</h3>
<pre><code class="language-bash">1
4
7
10
3
6
2
5
8
9
</code></pre>
<h2 id="执行流程图">执行流程图</h2>
<p>JS 代码逐行执行,在遇到宏任务时,整个代码块丢到宏任务队列,在遇到微任务时,将微任务丢到本次事件循环中的微任务队列,本次事件循环执行完之后,再执行微任务队列中的任务,微任务执行完之后开始下一个宏任务执行。</p>
<p><strong>JS 代码执行机制:</strong></p>
<p><img src="https://img2024.cnblogs.com/blog/596097/202506/596097-20250623094524832-1822095887.png"></p>
<p><strong>宏任务执行机制:</strong></p>
<p><img src="https://img2024.cnblogs.com/blog/596097/202506/596097-20250623094536697-387311508.png"></p>
<h2 id="写在最后">写在最后</h2>
<p>JS 中的代码执行流程永远都是事件循环机制,这是 JS 的任务调度核心,理解事件循环机制,才能在开发中游刃有余~~</p>


</div>
<div id="MySignature" role="contentinfo">
    <p>&nbsp;</p>
<p style="font-size: 18px;font-weight: bold;">文章首发于微信公众号【<span style="color:rgb(255, 71, 87)">前端路引</span>】,欢迎 <span style="color:#4ec259">微信扫一扫</span> 查看更多文章。</p>
<p>
<img style="max-width: 320px;" src="https://images.cnblogs.com/cnblogs_com/linx/2447020/o_250228035031_%E5%85%AC%E4%BC%97%E5%8F%B7%E4%BA%8C%E7%BB%B4%E7%A0%81.png"/>
</p>
<p>本文来自博客园,作者:前端路引,转载请注明原文链接:https://www.cnblogs.com/linx/p/18943769</p>
<p>&nbsp;</p><br><br>
来源:https://www.cnblogs.com/linx/p/18943769
頁: [1]
查看完整版本: Web前端入门第 68 问:JavaScript 事件循环机制中的微任务与宏任务