"精解 JavaScript 中的 Event Loop:微任务、宏任务全掌握"

前言

JavaScript 是一门单线程语言,这意味着它一次只能执行一个任务。这是因为 JavaScript 在设计之初就是作为浏览器脚本语言而诞生的,它的主要用途是与用户交互、操作 DOM 等。如果 JavaScript 是多线程的话,那么可能会导致对共享数据的操作变得复杂,容易出现竞争条件(race condition)等问题。JS中出现了同步和异步。那么接下来介绍一下同步和异步。

同步(Synchronous)执行: 在同步执行中,代码会按照顺序一行一行地执行,每行代码都必须等待上一行代码执行完成后才能继续执行。这意味着如果某个操作需要花费很长时间,那么整个程序的执行会被阻塞,直到该操作完成。

javascript 复制代码
console.log('开始');
// 执行一个耗时的操作
for (let i = 0; i < 1000000000; i++) {
 // 做一些耗时的计算
}
console.log('结束');

上面的例子中,当执行耗时的操作时,JavaScript 引擎会一直忙于计算,直到计算完成后才会继续执行下一行代码。

异步(Asynchronous)执行 : 在异步执行中,代码不会等待某个操作完成才继续执行,而是先将这个操作交给其他部件处理,自己继续执行其他代码。当操作完成后,通过回调函数或者 Promise 等机制来通知执行结果。

javascript 复制代码
console.log('开始'); // 执行一个异步操作 
setTimeout(function()
{ console.log('异步操作完成'); }, 1000);
console.log('结束');

进程与线程

进程(Process):

  • 一个进程代表着系统中运行的一个程序实例,它拥有独立的内存空间、文件描述符、以及其他系统资源。
  • 每个进程都是相互独立的,它们之间不能直接共享内存数据,而需要通过进程间通信的方式来进行数据交换。

线程(Thread):

  • 线程是进程中的一个实体,它可以被系统调度并执行。一个进程中可以包含多个线程,这些线程共享同一进程的内存空间和系统资源。
  • 在浏览器中,JavaScript 代码是运行在一个单独的线程中,这个线程被称为"JavaScript 引擎线程"。JavaScript 引擎线程负责执行 JavaScript 代码,并且处理与 DOM、CSSOM 相关的操作。

除了 JavaScript 引擎线程之外,浏览器还有其他一些线程,包括:

  1. GUI 渲染线程:负责渲染页面,处理与页面 UI 相关的操作。
  2. 事件触发线程:负责管理用户交互事件,比如鼠标点击、键盘输入等。
  3. 定时触发线程:负责处理定时器,以及一些与时间相关的操作。
  4. 异步 HTTP 请求线程:负责处理异步请求(如 Ajax 请求)的回调函数。

JS是单线程的优点

优点:

  1. 节约内存

  2. 没有锁的概念,节约上下文切换的时间

Event Loop

事件循环(Event Loop)是一种用于处理和调度异步操作的机制。它是在单线程环境下实现并发的关键组件。在一个应用程序中,可能存在多个异步任务,例如网络请求、文件读写操作等。这些任务不能立即完成,而是需要一定的时间。传统的编程模型中,我们会使用回调函数或者线程来处理这些异步任务,但是这样容易导致代码复杂、难以维护。

事件循环通过将所有的异步任务添加到一个任务队列中,并按照顺序依次执行这些任务,从而实现了异步操作的调度。它采用了事件驱动的方式,当某个任务完成时,会触发相应的回调函数。事件循环不断地从任务队列中取出任务执行,直到所有任务都完成。

微任务(microtask)和宏任务(macrotask)是指事件循环中的两种不同类型的任务。

  • 宏任务 (macrotask):script(整体代码)、setTimeout、setInterval、setImmediate'I/O、UI-rendering交互事件
  • 微任务 (microtask):Promise.then、MutationObserver,process.nextTick()

# 事件循环Event Loop执行机制

  1. 执行同步代码(这个属于宏任务)
  2. 当执行栈为空时,查询是否有异步需要执行
  3. 执行微任务
  4. 如果有需要,会渲染页面
  5. 执行宏任务( 下一次event-loop)

我来测试一下## Event Loop执行机制

javascript 复制代码
console.log(1);//1

setTimeout(() => {
    console.log(2);
    new Promise((resolve) => {
        console.log(4);
        resolve()
        setTimeout(() => {
            console.log(6);
        })
    }).then(() => {
        console.log(5);
    })
}, 1000)

console.log(3);

第一步执行同步代码,打印1,setTimeout是宏任务,要放进宏任务栈里面去,在打印3 第二部没有微任务,在开始一个循环执行打印2 在打印4 在打印 5 在打印6

注意选项

await

javascript 复制代码
console.log('stard');
async function async1() {
    await async2()//浏览器给await开小灶啦
    console.log('saync1 end');// 被await挤入微任务
}
async function async2() {
    console.log('saync2 end');
}
async1()

await右边的表达式会立即执行,浏览器给await开小灶啦,表达式之后 被await挤入微任务, await微任务可以转换成等价的promise微任务分析

执行同步代码(这个属于宏任务)

xml 复制代码
<script>
  console.log('script start');
  setTimeout(function () {
      console.log('setTimeout')
  }, 1000);
  console.log('script ');
</script>
<script>
  console.log('script end');
 
</script>

打印的结果为 'script start' ** 'script '** 'script end' 'setTimeout' 看到这里 大家应该把事件循环学的差不多啦吧 我来出一道题给大家试一下

大家把答案写在评论区吧 喜欢的来个关注 点赞 这个也是以后写文章的动力所在 谢谢大家能观看我的文章 咱下期再见 拜拜

相关推荐
测试199812 分钟前
2025软件测试面试题汇总(接口测试篇)
自动化测试·软件测试·python·测试工具·面试·职场和发展·接口测试
泯泷12 分钟前
「译」为 Rust 及所有语言优化 WebAssembly
前端·后端·rust
LinXunFeng18 分钟前
Flutter - GetX Helper 如何应用于旧页面
前端·flutter·开源
紫薯馍馍35 分钟前
Dify创建 echarts图表 (二)dify+python后端flask实现
前端·flask·echarts·dify
梦想很大很大1 小时前
把业务逻辑写进数据库中:老办法的新思路(以 PostgreSQL 为例)
前端·后端·架构
李三岁_foucsli1 小时前
从生成器和协程的角度详解async和await,图文解析
前端·javascript
星垂野1 小时前
JavaScript 原型及原型链:深入解析核心机制
javascript·面试
柚子8161 小时前
CSS自定义函数也来了
前端·css
zayyo1 小时前
面试官问我,后端一次性返回十万条数据,前端应该怎么处理 ?
前端·javascript·面试
Ai财富密码1 小时前
【Linux教程】Linux 生存指南:掌握常用命令,避开致命误操作
java·服务器·前端