一篇文章搞明白js运行机制——事件循环

news2024/11/14 16:20:41

1、解释 JavaScript 的执行机制。

        JavaScript 的执行机制基于事件循环。事件循环包括一个任务队列(Task Queue)和一个微任务队列(Microtask Queue)。当一个函数被调用时,它被添加到微任务队列中。事件循环每次迭代都会先执行微任务队列中的所有任务,然后执行一个宏任务(Macrotask),例如脚本代码、setTimeout、setInterval、setImmediate、I/O、UI 渲染等。当一个宏任务执行完成后,事件循环会再次检查微任务队列,并执行所有的微任务。这个过程会一直循环下去,因此被称为“事件循环”。

2、解释 JavaScript 中的事件循环。

        事件循环是 JavaScript 的核心运行机制,它负责调度和执行所有的任务。事件循环包括一个任务队列(Task Queue)和一个微任务队列(Microtask Queue)。当一个函数被调用时,它被添加到微任务队列中。事件循环每次迭代都会先执行微任务队列中的所有任务,然后执行一个宏任务(Macrotask),例如脚本代码、setTimeout、setInterval、setImmediate、I/O、UI 渲染等。当一个宏任务执行完成后,事件循环会再次检查微任务队列,并执行所有的微任务。这个过程会一直循环下去,因此被称为“事件循环”。

3、解释 JavaScript 中的任务队列和微任务队列。

        任务队列(Task Queue)和微任务队列(Microtask Queue)都是事件循环中的重要概念。任务队列用于存储宏任务(Macrotask),例如脚本代码、setTimeout、setInterval、setImmediate、I/O、UI 渲染等。当一个宏任务被添加到任务队列中时,它会在下一个事件循环中被执行。微任务队列用于存储微任务(Microtask),例如 Promise 的回调函数、process.nextTick 等。微任务会在当前事件循环的宏任务执行完成后立即执行,无论它们何时被添加到微任务队列中。

4、为什么 setTimeout(fn, 0) 的执行会发生在下一轮事件循环?

        setTimeout(fn, 0) 的执行会发生在下一轮事件循环,因为 setTimeout 将函数 fn 添加到任务队列中,而任务队列中的宏任务会在下一个事件循环中被执行。由于 setTimeout 的延迟参数为 0,所以 fn 会在下一个事件循环中被立即执行。

5、什么是宏任务和微任务?它们之间的区别是什么?

        宏任务(Macrotask)和微任务(Microtask)是事件循环中的两种不同类型的任务。
宏任务包括整个脚本代码、setTimeout、setInterval、setImmediate、I/O、UI 渲染等。
微任务包括 Promise 的回调函数、process.nextTick 等。
        它们之间的区别在于执行时机和执行顺序。每次事件循环迭代时,都会执行一个宏任务和所有的微任务,但是宏任务的执行会打断微任务的执行。因此,微任务的执行优先级高于宏任务。

6、解释一下 JavaScript 中的 Promise.then 和 Promise.catch 是如何工作的?它们和事件循环有什么关系?

        Promise.then 和 Promise.catch 是 JavaScript 中处理异步操作的重要函数。Promise 对象代表一个异步操作的最终完成(或失败)及其结果值。

Promise.then 方法返回一个新的 Promise 对象,称为 thenable。thenable 表示一个可以像 Promise 一样进行处理的类似 Promise 对象。当 Promise 完成时,thenable 的回调函数将被触发。

Promise.catch 方法返回一个 Promise,该 Promise 在第一个拒绝的 Promise 或 thenable 拒绝时触发。

Promise.then 和 Promise.catch 都与事件循环有关。在 JavaScript 的执行模型中,有一个任务队列(task-queue)和一个微任务队列(microtask-queue)。当一个异步事件(如定时器、Ajax 请求)完成后,它的回调函数会被添加到微任务队列中。在下一次循环中,JavaScript 会先处理微任务队列中的所有任务,然后再处理任务队列中的任务。Promise.then 和 Promise.catch 的回调函数会在微任务队列中等待执行。当 Promise 完成或被拒绝时,相应的回调函数会被添加到微任务队列中,并在下一次循环中执行。

7、解释一下 JavaScript 中的 MutationObserver 和它与事件循环的关系。

        MutationObserver 是一个提供监控 DOM 树更改的能力的接口。它被设计为在 DOM 树更改时提供低延迟的通知。MutationObserver 与事件循环的关系在于它的回调函数会在微任务队列中等待执行。当 DOM 树发生更改时,MutationObserver 的回调函数会被添加到微任务队列中,并在下一次循环中执行。

8、描述一下 JavaScript 中的 call stack 和 event loop。

        在 JavaScript 中,执行环境(Execution Context)是用于描述 JavaScript 代码运行的环境的概念。当 JavaScript 代码被执行时,会创建一个新的执行环境,并且该环境会一直存在直到代码执行完成。执行环境主要包括 call stack 和 heap。

call stack 是一个后入先出(LIFO)的数据结构,用于存储当前执行中的函数调用。当一个函数被调用时,它被添加到 call stack 的顶部。当函数执行完成时,它从 call stack 中移除。

event loop 是一个循环的过程,它会不断地检查 call stack 是否为空。如果 call stack 不为空,event loop 会从 call stack 中取出一个任务(可以是宏任务也可以是微任务)执行,然后将结果添加到微任务队列中。然后,event loop 会检查微任务队列是否为空。如果微任务队列不为空,event loop 会从微任务队列中取出所有的微任务并执行它们。然后,event loop 会再次检查 call stack 是否为空,并重复这个过程。

9、什么是 JavaScript 的事件循环?请简述其工作原理。

         事件循环是 JavaScript 的核心执行机制。它由一个或多个循环组成,每个循环称为一次循环或一次迭代。在每次循环中,事件循环会按照一定的顺序执行一系列的任务,包括处理用户交互、执行脚本代码、处理网络请求等。

事件循环的工作原理如下:

  1. 执行单次循环:事件循环开始执行单次循环。在单次循环中,事件循环会按照一定的顺序执行一系列的任务,包括处理用户交互、执行脚本代码、处理网络请求等。这些任务被称为宏任务(macro-tasks),包括 script(整体代码)、setTimeout、setInterval、setImmediate、I/O、UI rendering 等。在单次循环的末尾,事件循环会检查微任务队列(microtask-queue)。
  2. 处理微任务:如果微任务队列中有任务,事件循环会立即执行所有的微任务,直到微任务队列为空。微任务包括 Promise.then、process.nextTick、MutationObserver 等。在单次循环的末尾处理微任务是很重要的,因为它们通常与异步操作相关联,而这些操作需要在当前的同步操作之后立即执行。
  3. 检查终止条件:如果当前的单次循环已经执行了所有的宏任务和微任务,并且没有其他的同步操作需要执行,那么事件循环就会结束当前的单次循环并开始下一个单次循环。否则,事件循环会继续等待下一个宏任务到达并开始下一个单次循环。

10、事件循环中的 microtask 和 macrotask 有什么区别?

在 JavaScript 的事件循环中,任务被分为两种类型:microtask 和 macrotask。

Macrotask:Macrotask 是指那些需要较长时间才能完成的任务,例如 script(整体代码)、setTimeout、setInterval、setImmediate、I/O、UI rendering 等。

Microtask:Microtask 是一种 JavaScript 任务,它的执行优先级高于 macrotask。Microtask 包括但不限于 Promise 的回调函数、MutationObserver 的回调函数等。

事件循环中的 microtask 和 macrotask 的主要区别在于它们的执行优先级和执行时间。Macrotask 会等待同步代码执行完毕后,再在下一次事件循环中执行,而 microtask 会在同步代码执行完毕后立即执行,无论是否还有 macrotask 等待执行。因此,microtask 的执行优先级高于 macrotask。

11、JavaScript 中的 Promise 和 microtask 有何关系?

Promise 和 microtask 有密切的关系。当 Promise 的状态发生改变时(即从 pending 变为 resolved 或 rejected),会触发 microtask 队列的执行。这意味着,Promise 的回调函数(无论是 .then 还是 .catch)都会被添加到 microtask 队列中,并在下一次事件循环中执行。

因此,我们可以利用 Promise 和 microtask 的关系来确保在异步操作完成之后执行的代码能够立即执行,而不需要等待当前的同步代码执行完毕。例如,如果我们在一个 Promise 的回调函数中进行异步操作,那么这个异步操作也会被添加到 microtask 队列中,并在下一次事件循环中执行。因此,我们可以利用这个特性来确保在异步操作完成之后执行的代码能够立即执行,而不需要等待当前的同步代码执行完毕。


案例

1.微任务:Promise 回调函数,宏任务:setTimeout 回调函数

console.log('start');
setTimeout(() => console.log('setTimeout'), 0);
Promise.resolve().then(() => console.log('Promise'));
console.log('end');

// start
// end
// Promise
// setTimeout

解析: 首先输出 start,然后通过 setTimeout 方法注册了一个回调函数,它会被添加到宏任务队列中。接着创建了一个 Promise 实例,并且通过 then 方法注册了一个回调函数,在 Promise 对象的状态改变时会执行这个回调函数。接着输出 end。因为 Promise 回调函数是微任务,所以它会被添加到微任务队列中,等待执行。等到主线程的同步任务执行完毕后,JavaScript 引擎会先执行微任务队列中的任务,输出 Promise,然后执行宏任务队列中的任务,输出 setTimeout

2.微任务:process.nextTick 回调函数,宏任务:setImmediate 回调函数

console.log('start');
setImmediate(() => console.log('setImmediate'));
process.nextTick(() => console.log('process.nextTick'));
console.log('end');

// start
// end
// process.nextTick
// setImmediate

解析: 在 Node.js 环境中,process.nextTick 回调函数是排在微任务队列最前面的,优先级比 Promise 回调函数还要高。而 setImmediate 回调函数是排在宏任务队列最后面的。所以,以上代码会先输出 start,然后输出 end。等到主线程的同步任务执行完毕后,JavaScript 引擎会先执行微任务队列中的任务,输出 process.nextTick,然后执行宏任务队列中的任务,输出 setImmediate

3.微任务:Promise 回调函数,宏任务:requestAnimationFrame 回调函数

console.log('start');
requestAnimationFrame(() => console.log('requestAnimationFrame'));
Promise.resolve().then(() => console.log('Promise'));
console.log('end');

// start
// end
// Promise
// requestAnimationFrame

解析: 首先输出 start,然后通过 requestAnimationFrame 方法注册了一个回调函数,它会被添加到宏任务队列中。接着创建了一个 Promise 实例,并且通过 then 方法注册了一个回调函数,在 Promise 对象的状态改变时会执行这个回调函数。接着输出 end。因为 Promise 回调函数是微任务,所以它会被添加到微任务队列中,等待执行。等到主线程的同步任务执行完毕后,JavaScript 引擎会先执行微任务队列中的任务,输出 Promise,然后执行宏任务队列中的任务,输出 requestAnimationFrame

4.微任务:Promise 回调函数,宏任务:XMLHttpRequest 回调函数

console.log('start');
const xhr = new XMLHttpRequest();
xhr.open('get','http://localhost:4000/get',true)
xhr.onload = () => console.log('XMLHttpRequest');
xhr.send();
Promise.resolve().then(() => console.log('Promise'));
console.log('end');

//start
//end
//Promise
//XMLHttpRequest

解析: 首先输出 start,然后创建了一个 XMLHttpRequest 对象,并且通过 open 方法和 send 方法发送了一个 GET 请求。接着通过 onload 方法注册了一个回调函数,在请求成功后会执行这个回调函数。接着创建了一个 Promise 实例,并且通过 then 方法注册了一个回调函数,在 Promise 对象的状态改变时会执行这个回调函数。接着输出 end。因为 Promise 回调函数是微任务,所以它会被添加到微任务队列中,等待执行。等到主线程的同步任务执行完毕后,JavaScript 引擎会先执行微任务队列中的任务,输出 Promise,然后等待 XMLHttpRequest 对象的回调函数执行。当请求成功后,JavaScript 引擎会执行宏任务队列中的任务,输出 XMLHttpRequest

5.最终的案例:

async function async1() {
    console.log('async1 start')
    await async2()
    console.log('async1 end')
}
async function async2() {
    console.log('async2')
}
console.log('script start')
setTimeout(function () {
    console.log('settimeout')
})
async1()
new Promise(function (resolve) {
    console.log('promise1')
    resolve()
}).then(function () {
    console.log('promise2')
})
console.log('script end')


script start、async1 start 、saync2、promise1、script end、async1 end、promise2、settimeout

解析

1.执行整段代码,第一次执行同步任务:log(script start)

2.遇到定时器为异步任务先不执行

3.遇到async1()执行 打印async1 start 然后执行await 打印async2之后阻塞async1 end将其加入微任务列表 

4.跳出函数继续向下执行 碰到new promise执行 打印promise1 之后遇到.then为微任务加入为微物列表

5.跳出后执行最后一行代码 打印script end 至此第一轮宏任务执行完毕 开始执行微任务

6.此时微任务列表按顺序打印 async1 end、promise2微任务 至此微任务执行完毕

7.开启新一轮的事件循环 开始执行下一个宏任务 即定时器 打印settimeout

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

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

相关文章

分布式数据库Schema 变更 in F1 TiDB

分布式数据库Schema 变更 in F1 & TiDB 【转载】TiDB 源码阅读系列文章(十七)DDL 源码解析 | PingCAP 上述文章主要叙述了从DDL语句发起到执行的过程,简单介绍了弄一套相同的模式来后台处理数据回填,从而提高DDL的并发度的一…

Excel函数-将A1中的字符串剔除B1中的字符串

比如A2中是类型单位,B2中是单位,在C2中体现A2-B2的结果,即大米 公式:SUBSTITUTE(A2,B2,“”) SUBSTITUTE函数功能:将字符串中的部分字符用新字符替换,替换序号忽略说明进行全部替换 结构:SUB…

如何加密RAR压缩文件?

如何使用WinRAR加密压缩包?详细介绍WinRAR中的三种加密方法给大家。 方法一:加密 最简单的加密方法,就是在加密文件时输入想要设置的密码,完成加密和压缩了。 方法二:自动加密 普通的加密方式,需要我们加…

NC65 客户、供应商、人员等基本信息校验唯一性规则的设置

NC65 客户、供应商、人员等基本信息校验唯一性规则的设置 供应商名称唯一校验,很容易导致重名的无法保存,那如何设置得如下图提示的按名称基本分类纳税人登记号等校验规则呢? 答:使用 admin 账号登录,然后在基础数据…

基于《环境影响评价技术导则大气环境(HJ 2.2-2018)》的AERMOD模型配置方法

数值模式模拟是分析大气污染物时空分布和成分贡献的重要工具,利用模拟结果可以分析大气污染的来源、成因、污染程度、持续时间、主要成分、相对贡献等问题,有助于分析并合理控制污染源排放,为产业调整提供参考。当前,针对不同理论…

51单片机应用从零开始(二)

目录 1. 什么是单片机系统 1.1 单片机本身 1.2 构成单片机系统——单片机外围器件 2. 如何控制一个发光二极管 2.1 硬件设计(系统电路图 ) 2.2 硬件设计(搭建硬件电路的器材 ) 2.3 软件设计(中文描述的程…

JAVA基础:子父类关系里的实例创建流程

实验类: 📎A.javahttps://www.yuque.com/attachments/yuque/0/2023/java/21609500/1699858993581-1df32da6-8360-4a98-aa1b-d9a59d3b2d76.java 📎B.javahttps://www.yuque.com/attachments/yuque/0/2023/java/21609500/1699858998289-d9e31…

做职业规划,离不开职业兴趣和职业性格测评

每天上班,但是没有动力,职业很迷茫是典型的症状,如果这个症状持续超过2周,那么这个问题就值得思考了。比如昨天就遇到过一个朋友说,希望自己能早点改变这个现状..... 有这个想法是非常正确的,职业规划越早…

RK3568驱动指南|第七期-设备树-第65章 设备树下platform_device和platform_driver匹配实验

瑞芯微RK3568芯片是一款定位中高端的通用型SOC,采用22nm制程工艺,搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码,支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU,可用于轻量级人工…

深入探讨跨境电商商城源码的多语言、多货币、多商户入驻支持“

随着全球电子商务的飞速发展,跨境电商已成为一股不可忽视的力量。在这个背景下,为跨境电商商城源码提供多语言、多货币、多商户入驻支持显得尤为重要。本文将深入探讨这三大特性的实现方式及其对跨境电商发展的影响。 一、多语言支持 对于跨境电商来说&a…

Outlook如何删除邮箱账户

Outlook如何删除邮箱账户 说明: 最近有用户询问到“我的Outlook登陆了很多个邮箱账号,不知道怎么退出”接下来将具体操作步骤加以说明 操作指引: 1、首先打开Outlook该软件,然后点击“文件” 2、点击账户设置下拉菜单 3、在下拉…

SSM德庆县乡村教育图书管理系统-计算机毕设 附源码 24668

SSM德庆县乡村教育图书管理系统 摘 要 大数据时代下,数据呈爆炸式地增长。为了迎合信息化时代的潮流和信息化安全的要求,利用互联网服务于其他行业,促进生产,已经是成为一种势不可挡的趋势。在德庆县乡村教育图书管理的要求下&…

海思平台awb标定

文章目录 1、raw图采集2、awb标定3、标定效果优化1、raw图采集 raw图采集标准: 如果raw是12bit,即raw12,那么Block9 块的亮度就是40960.8 = 3276.8左右。 勾上Dump Raw,我这里raw10,即Depth为10bit,那么Block19的亮度应该为10240.8 = 819.2左右,调整曝光Exposure Attr…

制造业工厂MES系统中的设备管理模块

随时工厂数字化建设的大力推进,设备管理的效率得到了很大的提升,特别是作为机加工企业,设备是整个企业非常重要的核心资产。下面是万界星空科技MES系统中的设备管理模块介绍: 1、MES设备管理任务模型 制造企业总是期望设备能够在…

LED显示屏老化知识

LED显示屏老化是指长时间使用后,LED显示屏性能逐渐下降和衰减的过程。虽然LED显示屏具有较长的寿命和良好的稳定性,但长期使用和环境因素会导致一定程度的老化现象。 LED显示屏为什么会老化 1. 亮度衰减:LED显示屏使用时间越长,LE…

消失的它:摆脱 SwiftUI 中“嵌入视图数量不能超过 10 个”限制的秘密

概览 SwiftUI 带来了描述性界面布局的新玩法,让我们可以超轻松的创建复杂应用界面。但是在早期 SwiftUI 中有一个“著名”的限制大家知道么?那就是 ViewBuilder 中嵌入子视图数量不能超过 10 个! 不过,从 Swift 5.9 开始这一“桎…

PHP判断扫码支付扫码条码支付宝微信区分

微信&#xff1a;用户付款码规则&#xff1a;18位纯数字&#xff0c;前缀以10、11、12、13、14、15开头 支付宝&#xff1a;25~30开头的长度为16~24位的数字&#xff0c;实际字符串长度以开发者获取的付款码长度为准 <?php /*** 判断扫码支付的方式* param string $code 扫…

前后端分离项目为什么很火?有什么优势?

目录 一、什么是前后端分离 二、前后端分离项目的技术栈 三、前后端分离项目有什么优势 一、什么是前后端分离 前后端分离是一种软件架构的设计模式,它将应用程序的前端&#xff08;即用户界面&#xff09;和后端&#xff08;即服务器端&#xff09;进行解耦,使得它们可以独…

HTML简单介绍

且视他人之疑目如盏盏鬼火&#xff0c;大胆地去你的夜路。 1.网页 组成&#xff1a;文字&#xff0c;图片&#xff0c;音频&#xff0c;视频&#xff0c;超链接 2.Web标准 3.HTML 超文本标记语言 3.1HTML结构 网页可以看成是一篇文章 如&#xff1a;整体&#xff0c;头部…

【KVM-4】硬件虚拟化技术(详)

前言 大家好&#xff0c;我是秋意零。 经过前面章节的介绍&#xff0c;已经知道KVM虚拟化必须依赖于硬件辅助的虚拟化技术&#xff0c;本节就来介绍一下硬件虚拟化技术。 &#x1f47f; 简介 &#x1f3e0; 个人主页&#xff1a; 秋意零&#x1f525; 账号&#xff1a;全平…