js事件循环机制(event loop) 同步任务 异步任务 宏任务 微任务 任务队列与调用栈

news2024/10/1 13:36:41

1 js事件循环机制简单介绍

        JavaScript 是单线程的,处理耗时任务时会阻塞程序执行。为解决这一问题,引入了异步任务机制。同步任务由 JavaScript 引擎直接处理,而异步任务则由宿主环境(如 Node.js、浏览器等多线程环境)执行。同步任务会立即加入执行栈并等待结果,而异步任务则在适当时机(例如定时器结束或点击事件发生时)被放入任务队列。当执行栈中的任务完成后,事件循环会检查任务队列中是否有待处理的任务,并将其逐一加入执行栈,循环往复,这便构成了事件循环机制。

 1.1 宏任务与微任务队列

        异步任务队列又被分为了两种不同的类型队列,分别是宏任务(Macrotasks)和微任务(Microtask)任务队列。

宏任务

        宏任务是较大粒度的任务,通常对应于整体事件或动作。每个事件循环周期(Event Loop Cycle)中,事件循环会从宏任务队列中取出一个任务并执行(其实我们顶层的整个script也是一个宏任务)。

常见的宏任务类型
  • script代码块(我们的脚本也是一个宏任务)
  • setTimeout 和 setInterval:用于设置定时器,延迟执行代码。
  • I/O 操作:如文件读取、网络请求等(在Node.js中)。
  • UI 渲染事件:如 resize、scroll 等浏览器事件。
  • 用户交互事件:如 click、keydown 等。

微任务

         微任务是较小粒度的任务,通常用于在当前宏任务执行完毕后、下一个宏任务开始前执行一些需要尽快完成的操作。微任务队列的优先级高于宏任务队列。

常见的微任务类型
  • Promise 的回调:如 then、catch、finally。
  • MutationObserver:用于监测DOM变化。
  • process.nextTick(Node.js特有):用于在当前操作完成后立即执行。
  • async 和 await 特别是await后面的代码可以看作callback放入微任务队列

 事件循环机制中的执行顺序

  1. 从宏任务队列中取出第一个宏任务并执行。
  2. 执行所有微任务队列中的任务,直到微任务队列为空。
  3. 进行渲染(如果需要)。
  4. 重复以上步骤,进入下一个事件循环周期。

1.2 事件循环机制举例说明

        下面看一下示例代码1: 

// 示例代码01
console.log('script start');

setTimeout(() => {
    console.log('setTimeout');
}, 0)

console.log('script end');

        输出结果如下所示:

script start
script end
setTimeout
  1.  首先执行整个script,console.log是同步的,直接输出script start
  2.  setTimeout是新的宏任务,放入宏任务队列,继续执行余下script代码
  3.  console.log是同步的,直接输出script end
  4.  执行栈为空,微任务队列为空,取出宏任务的回调函数进行执行
  5.  输出setTimeout

         下面看一下示例代码2,我们将引入微任务:

// 示例代码02
console.log('script start')

const p = new Promise((resolve)=>{
    console.log('promise executor')
    resolve(2)
    console.log('promise executor end')
})

p.then((res) => {
    console.log(res)
})

setTimeout(() => {
    console.log('setTimeout')
})

console.log('script end')

        输出结果如下所示:

script start
promise executor
promise executor end
script end
2
setTimeout
  1. 首先输出同步内容,script start,在创建Promise对象时传入的回调函数也是同步对象,会立即执行,输出promise excutor promise executor end。
  2. .then 和 .catch都是异步的微任务,将其放入微任务队列。
  3. setTimeout是宏任务队列,放入宏任务队列。
  4. 最后将console.log放入执行栈执行,输出script end,当前宏任务执行完毕,查看微任务队列。
  5. 取出微任务队列的then函数的回调函数,执行输出 2 ,微任务队列执行完毕,取出下一个宏任务执行。
  6. 执行宏任务队列中的setTimeout输出setTimeout

        下面看最复杂的示例代码3:

// 示例代码03
async function async1() {
    console.log('async1 start')

    new Promise((resolve) => {
        resolve('promise1')

        setTimeout(() => {
            resolve('setTimeout 2')
            console.log('setTimeout 2')
        }, 0)

        new Promise((resolve) => {
            
            setTimeout(() => {
                resolve('setTimeout 3')
                console.log('setTimeout 3')
            }, 0)
        }).then((res) => {
            console.log(res)
        })
    }).then((res) => {
        console.log(res)
    })
    await async2()
    console.log('async1 end')
}

async function async2() {
    console.log('async2')
}

console.log('script start')

setTimeout(()=>{
    console.log("setTimeout 1")
}, 0)

async1()
Event loop 1
  1.  执行script,首先console.log放入执行栈输出 script start
  2. 宏任务setTimeout放入宏任务队列, 遇到异步方程,继续执行
  3. 执行异步函数async 1,首先输出async1 start进入后执行第一个Promise的回调函数,将setTimeout放如宏任务队列。
  4. 进入第二个嵌套的Promise的回调函数,添加setTimeout[setTimeout 3]
  5. 添加第二个Promise的then回调函数到微任务队列
  6. 碰到await 直接执行右侧函数,console.log放入执行栈输出 async2
  7. await之后的代码被视为then回调中的函数,放入微任务队列。

微任务队列:then(pending) then(promise 1) await(...async1 end)

宏任务队列:setTimeout[setTimeout 1] setTimeout[setTimeout 2] setTimeout[setTimeout 3]

Event loop 2
  1. 优先执行微任务,第一个微任务处于pending状态跳过,执行第一个promise的回调,输出promise1
  2. 执行await后的余下代码,输出async1 end,微任务执行完毕,执行下一个宏任务
  3. 取出setTimeout[setTimeout 1],输出setTimeout 1
  4. 宏任务结束,进入下一个事件循环

微任务队列:then(promise 2 pending)

宏任务队列:setTimeout[setTimeout 2] setTimeout[setTimeout 3]

 Event loop 3
  1. 微任务队列中的then回调依然处于pending,等待resolve触发执行,取出setTimeout [setTimeout 2] 执行(注意多次调用resolve是无效的),输出setTimeout 2
  2. 宏任务执行完毕,该循环结束。

微任务队列:then(promise 2 pending)

宏任务队列:setTimeout[setTimeout 3]

Event loop 4
  1. 微任务队列中的then回调依然处于pending,等待resolve触发执行,取出setTimeout[setTimeout 3]的回调函数执行。
  2. resolve("setTimeout")将会触发then的callback的执行,输出setTimeout 3
  3. console.log输出setTimeout 3
  4. 任务队列结束,代码执行完毕

微任务队列:empty

宏任务队列:empty

最终输出结果 
  1.  script start
  2.  async1 start
  3.  async2
  4.  promise1
  5.  async1 end
  6.  setTimeout 1
  7.  setTimeout 2
  8.  setTimeout 3
  9.  setTimeout 3

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2182439.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Leetcode 每日一题:Crack The Safe

写在前面: 学期真的忙起来了,我的两个社团也在上一周终于完成了大部分招新活动。虽然后面有即将到来的期中考试和求职,但希望能有时间将帖子的频率提上去吧(真的尽量因为从做题思考到写博客讲解思路需要大量的时间,在…

矩阵系统源码搭建的具体步骤,支持oem,源码搭建

一、前期准备 明确需求 确定矩阵系统的具体用途,例如是用于社交媒体管理、电商营销还是其他领域。梳理所需的功能模块,如多账号管理、内容发布、数据分析等。 技术选型 选择适合的编程语言,如 Python、Java、Node.js 等。确定数据库类型&…

盘点2024年远程控制黑科技,4款好用到飞起,你get了吗?

随着数字化的浪潮,远程办公变得越来越流行。虽然有些人担心不在办公室工作,效率会降低,但实际上并不是这样。技术一直在进步,现在有很多好用的远程控制软件,它们不仅打破了地点的限制,还让在家工作也能井井…

Spring Boot实现足球青训俱乐部管理自动化

4 系统设计 4.1 系统架构设计 B/S系统架构是本系统开发采用的结构模式,使用B/S模式开发程序以及程序后期维护层面需要的经济成本是很低的,用户能够承担得起。使用这样的模式开发,用户使用起来舒心愉悦,不会觉得别扭,操…

Spring Boot技术在足球青训管理中的创新应用

3 系统分析 3.1 可行性分析 可行性分析是该平台系统进行投入开发的基础第一步,必须对其进行可行性分析才能够降低不必要的需要从而使资源合理利用,更具有性价比和降低成本,同时也是系统平台的成功的未雨绸缪的一步。 3.1.1 技术可行性 技术可…

Redis篇(缓存机制 - 多级缓存)(持续更新迭代)

目录 一、传统缓存的问题 二、JVM进程缓存 1. 导入案例 2. 初识Caffeine 3. 实现JVM进程缓存 3.1. 需求 3.2. 实现 三、Lua语法入门 1. 初识Lua 2. HelloWorld 3. 变量和循环 3.1. Lua的数据类型 3.2. 声明变量 3.3. 循环 4. 条件控制、函数 4.1. 函数 4.2. 条…

足球青训后台管理系统:Spring Boot实现指南

2 相关技术简介 2.1 Java技术 Java是一门伟大的纯面向对象的编程语言和编程语言。同时,它还是Java语言从嵌入式开发到企业级开发的平台。Java凭借其一次编译,任何地方执行的优点,使得盛行的web应用程序有大量的Java编译,很好地支…

生信初学者教程(十九):免疫浸润细胞

文章目录 介绍加载R包导入数据所需函数运行ImmuCellAI其他免疫浸润方法输出结果总结介绍 免疫浸润分析在癌症研究中扮演着至关重要的角色,它有助于理解癌症微环境中免疫细胞的组成及其作用。bulk转录组基因表达数据的反卷积技术,如CIBERSORT算法,是实现这一分析的重要工具。…

云原生数据库 PolarDB

简介:云原生数据库 PolarDB 是阿里云自研产品,在存储计算分离架构下,利用了软硬件结合的优势,为用户提供秒级弹性、高性能、海量存储、安全可靠的数据库服务。100%兼容MySQL和PostgreSQL生态,支持分布式扩展&#xff0…

Spring整合Mybatis Plus

Mybatis Plus是原始Mybatis的增强,框架内部自动实现了Mapper的CRUD操作,极大的提高了编程效率。对单表操作基本无需编写Mapper.xml文件内容,对复杂的多表关联查询时,需要额外在Mapper.xml编写对应的sql语句。 Spring整合Mybatis P…

《如何高效学习》

有道云笔记 第一部分 整体性学习策略 结构 结构就像思想中的一座城市,有很多建筑物,建筑物之间有道路相连,有高大而重要的与其他建筑有上百条路相连,无关紧要的建筑只有少数泥泞的小道与外界相通。 建立良好的知识结构就是绘制…

仿真设计|基于51单片机的土壤温湿度监测及自动浇花系统仿真

目录 具体实现功能 设计介绍 51单片机简介 资料内容 仿真实现(protues8.7) 程序(Keil5) 全部内容 资料获取 具体实现功能 (1)DS18B20实时检测环境温度,LCD1602实时显示土壤温湿度&…

【C++】vector详解:接口使用、迭代器、内存理解、与模拟实现

文章目录 1. 前言2. 内存角度 理解3. vector的使用定义 | 构造函数vector iteratorvector 空间增长问题vector 增删查改vector 迭代器失效避免迭代器失效的建议 4. 如何理解 二维动态vector5. 模拟实现 vector6. 相关文档 1. 前言 vector 是 C 标准模板库(STL&…

实例说明机器学习框架

机器学习框架是用于构建和训练机器学习模型的工具集合,它们提供了丰富的功能和库,帮助开发者简化模型开发流程。以下是几个流行的机器学习框架及其应用实例: 1. TensorFlow TensorFlow 是由 Google 开发的开源机器学习框架,广泛…

记一次使用python编写exp

使用的漏洞是企望制造ERP系统 RCE漏洞 POC POST /mainFunctions/comboxstore.action HTTP/1.1 Host: Cache-Control: max-age0 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.…

影刀RPA实战:Excel拆分与合并工作表

1.影刀操作excel的优势 Excel,大家都不陌生,它是微软公司推出的一款电子表格软件,它是 Microsoft Office 套件的一部分。Excel 以其强大的数据处理、分析和可视化功能而闻名,广泛应用于商业、教育、科研等领域。可以说&#xff0…

生信初学者教程(二十):免疫浸润分析

文章目录 介绍加载R包导入数据所需函数堆积图箱线图热图相关性矩阵图输出结果总结介绍 在本章节中,将详细探讨免疫细胞的组成结构、其在不同个体和分组之间的相对丰度差异,并通过热图等可视化手段,对这些差异进行直观而深入的解析。这些分析将有助于科研人员更好地理解免疫细…

828华为云征文|华为云 Flexus X 实例之家庭娱乐中心搭建

话接上文《828华为云征文|华为云Flexus X实例初体验》,这次我们利用手头的 Flexus X 实例来搭建家庭影音中心和密码管理环境。 前置环境 为了方便小白用户甚至运维人员,我觉得现阶段的宝塔面板 和 1Panel 都是不错的选择。我这里以宝塔为例…

动态规划最低票价

前言&#xff1a;之前看到过这个题目归结到动态规划&#xff0c;当初还没什么思路&#xff0c;其实就是定义好dp [ i ] 为到第 i 个的最小费用就行&#xff0c;我们可以用upper_bound来优化我们的查找下标 题目地址 class Solution { public:int mincostTickets(vector<int&…

应对集运仓库丢件问题:集运系统的视频监控验货功能

在集运行业中&#xff0c;包裹丢件问题一直是令企业头疼的问题之一。客户投诉、纠纷处理不仅消耗了大量的人力物力&#xff0c;还可能影响企业的信誉和客户满意度。集运系统提供的视频验货服务&#xff0c;为解决这一难题提供了有效的解决方案。 一、集运仓库丢件问题的现状 集…