js事件循环机制 宏任务微任务执行时机

事件循环的工作步骤
  1. 先执行同步代码,所有同步代码都在主线程上执行,形成一个执行栈(又称调用栈,先进后出)。
  2. 当遇到异步任务时,会将其挂起并添加到任务队列中(先进先出),宏任务放入宏任务队列,微任务放进微任务队列。
  3. 当执行栈为空时,事件循环先从微任务队列中按顺序取出任务,加入到执行栈中执行。如果微任务队列清空,就从宏任务队列中取出任务加入执行栈中执行。
  4. 重复上述步骤,直到任务队列为空。
执行时机

微任务早于宏任务,每次从宏任务队列读取任务时,微任务队列都是已执行完毕清空的。

微任务执行过程中生成新的异步任务,将其放在对应的队列中,如果有生成新的微任务,会依次将所有的微任务执行完成后再执行宏任务。

宏任务在执行过程生成新的微任务时,那么这些微任务会被添加到微任务队列中,等待当前这个宏任务执行完成后依次执行微任务队列。当所有微任务都执行完成后,才会从宏任务队列中取出下一个任务执行。

调用栈

调用栈(call stack)也叫执行栈,是 JavaScript 运行时用于存储函数调用的数据结构(栈先进后出),它记录了当前执行的上下文(context)和函数调用链。

javascript 复制代码
function bar() {
  return 2;
}
function foo() {
  return bar();
}
function main() {
  console.log(foo());
}
main();

在执行 main() 函数时,它会调用 foo() 函数,将 foo() 函数的调用信息添加到调用栈中。在 foo() 函数中,又调用了 bar() 函数,将 bar() 函数的调用信息也添加到调用栈中。最后,在 bar() 函数中返回结果后,将 bar() 函数的调用信息从调用栈中弹出。接着执行 foo() 函数返回结果,再将 foo() 函数的调用信息从调用栈中弹出。最后,main() 函数也执行完毕,调用栈为空。在函数执行期间,调用栈会保持不断增长和收缩的状态。

任务队列

任务队列(task queue)通常分为两种类型:宏任务队列和微任务队列。只有异步代码才会进入任务队列,然后按照先进先出的顺序执行。

代码示例

javascript 复制代码
async function async1() {
  console.log("start"); // 第1轮
  await async2(); // 第1轮
  await async3(); // 第2轮 因为上一行的await,这里属于微任务,放在微任务队列
  console.log('async111 end'); // 第3轮 第2轮执行的时候,因为上一行的await,这里属于微任务,放在微任务队列,等待第3轮执行。
}
async function async2() {
  console.log('async222 end'); // 第1轮
}
async function async3() {
  console.log('async333 end'); // 第2轮
}
async1(); // 第一轮
setTimeout(() => {
  console.log('setTimeout1'); // 第4轮 放在宏任务队列,等待微任务队列清空后,执行
  new Promise(resolve=>{
  	console.log("setTimeout1->Promise") // 第4轮 同步代码
  	resolve(true)
  }.then(()=>{
  	console.log("setTimeout1->Promise->then") // 第5轮 微任务
  })
}, 0)
new Promise(resolve => {
  console.log('Promise'); // 第1轮,这里属于同步代码
  resolve()
}).then(() => {
  console.log('Promise111'); //第2轮,微任务
  setTimeout(() => 
  	console.log("setTimeout2"); // 第7轮,第2轮的时候,这个任务才被放在宏任务队列中,在setTimeout3之后执行。
  })
}).then(() => {
  console.log('Promise222'); // 第3轮 第2轮执行时,会将这里放在微任务队列中,等待第3轮执行
})
setTimeout(() => {
  console.log('setTimeout3'); // 第6轮 宏任务,本可以在第4轮宏任务一起执行,由于4生成了新的微任务,当4轮宏任务执行完毕后,要从微任务队列中执行微任务后(第5轮),再执行该宏任务
}, 0)
console.log('end'); // 第1轮
// start
// async222 end
// Promise
// end
// async333 end
// Promise111
// async111 end
// Promise222
// setTimeout1
// setTimeout1->Promise
// setTimeout1->Promise->then
// setTimeout3
// setTImeout2
// 第几轮的说法并不准确,仅作分析思路。

参考博客:
JavaScript------事件循环机制(Event Loop)浅析

相关推荐
阿伟来咯~15 分钟前
记录学习react的一些内容
javascript·学习·react.js
吕彬-前端20 分钟前
使用vite+react+ts+Ant Design开发后台管理项目(五)
前端·javascript·react.js
学前端的小朱23 分钟前
Redux的简介及其在React中的应用
前端·javascript·react.js·redux·store
guai_guai_guai32 分钟前
uniapp
前端·javascript·vue.js·uni-app
也无晴也无风雨33 分钟前
在JS中, 0 == [0] 吗
开发语言·javascript
王哲晓2 小时前
第三十章 章节练习商品列表组件封装
前端·javascript·vue.js
理想不理想v2 小时前
‌Vue 3相比Vue 2的主要改进‌?
前端·javascript·vue.js·面试
酷酷的阿云2 小时前
不用ECharts!从0到1徒手撸一个Vue3柱状图
前端·javascript·vue.js
aPurpleBerry3 小时前
JS常用数组方法 reduce filter find forEach
javascript
ZL不懂前端3 小时前
Content Security Policy (CSP)
前端·javascript·面试