事件循环的学习、执行上文、this、执行栈和任务队列

2023-10-29 02:36

本文主要是介绍事件循环的学习、执行上文、this、执行栈和任务队列,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

事件循环

对于js运行中的任务,js有一套处理收集,排队,执行的特殊机制,我们称之为事件循环(Even Loop).
🍕js一大特点就是单线程同一个时间只能做一件事。为了协调事件、用户交互、脚本、UI渲染和网络处理等行为,防止主线程的不阻塞,Event Loop 的方案应用而生。Even Loop 包含两类:Browsing Context,一种是基于 Worker.二者的运行是独立的,每个js运行的 “线程环境”都有一个独立的 Even Loop 。

单线程

🍔与他的用途有关。浏览器脚本语言,js的主要用途是与用户互动,以及操作DOM。这个决定了它只能是单线程,否则会带来很复杂的同步问题。
从诞生之日起就是一门单线程的非阻塞的脚本语言。最初的用途来决定的:与浏览器交互。
  • 🍟而非阻塞则是当代码需要进行一项异步任务(无法立刻返回结果,需要花一定时间才能返回的任务,如 I/O 事件)的时候,主线程会挂起(pending)这个任务,然后在异步任务返回结果的时候再根据一定规则去执行相应的回调。
  • 🚗🚗🚗单线程是必要的,也是 JavaScript 这门语言的基石,原因之一在其最初也是最主要的执行环境——浏览器中,我们需要进行各种各样的 dom 操作。试想一下 如果 JavaScript 是多线程的,那么当两个线程同时对 dom 进行一项操作,例如一个向其添加事件,而另一个删除了这个 dom,此时该如何处理呢?因此,为了保证不会 发生类似于这个例子中的情景,JavaScript 选择只用一个主线程来执行代码,这样就保证了程序执行的一致性。
  • 单线程在保证了执行顺序的同时也限制了 JavaScript 的效率,因此开发出了 web worker 技术。这项技术号称让 JavaScript 成为一门多线程语言。
  • 使用 web worker 技术开的多线程有着诸多限制,例如:所有新线程都受主线程的完全控制,不能独立执行。这意味着这些“线程” 实际上应属于主线程的子线程。另外,这些子线程并没有执行 I/O 操作的权限,只能为主线程分担一些诸如计算等任务。所以严格来讲这些线程并没有完整的功能,也因此这项技术并非改变了 JavaScript 语言的单线程本质。

另一个特点 “非阻塞”,和事件循环的关系

执行上下文

总共有三种类型

  • 全局执行上下文:只有一个,浏览器中的全局对象就是 window 对象,this 指向这个全局对象。
  • 函数执行上下文:存在无数个,在函数被调用的时候才会被创建,每次调用函数都会创建一个新的执行上下文。
  • Eval 函数执行上下文:指的是运行在 eval 函数中的代码,很少用而且不建议使用。

执行上下文的创建

创建阶段
  • 1、确定 this 的值,也被称为 This Binding。
  • LexicalEnvironment(词法环境) 组件被创建。
  • VariableEnvironment(变量环境) 组件被创建

this绑定

-全局执行上下文中,this 的值指向全局对象,在浏览器中 this 的值指向 window 对象,而在 nodejs 中指向这个文件的 module 对象。

  • 函数执行上下文中,this 的值取决于函数的调用方式。具体有:默认绑定、隐式绑定、显式绑定(硬绑定)、new 绑定、箭头函数,

词法环境

有两个组成部分 = 自己的 + 外部引用
  • 环境记录:存储变量和函数声明的实际位置
  • 对外部环境的引用:可以访问其外部词法环境

词法环境有两种类型

  • 全局环境:是一个没有外部环境的词法环境,其外部环境引用为 null。拥有一个全局对象(window 对象)及其关联的方法和属性(例如数组方法)以及任何用户自定义的全局变量,this 的值指向这个全局对象。
  • 函数环境:用户在函数中定义的变量被存储在环境记录中,包含了 arguments 对象。对外部环境的引用可以是全局环境,也可以是包含内部函数的外部函数环境。

变量环境

  • 变量环境也是一个词法环境,因此它具有上面定义的词法环境的所有属性,在 ES6 中,他们的区别在于
  • 词法环境用于存储函数声明和变量( let 和 const )绑定,变量环境仅用于存储变量( var )绑定。
  • 变量提升和函数提升的原因:在创建阶段,函数和变量的声明已经存储在了执行上下文中,var声明的变量会被设置为 undefined,而在 let 和 const 的情况下,则会保持未初始化。

三、执行栈与任务队列

执行栈(Execution Stack)

  • 执行栈,也叫调用栈,具有 LIFO(后进先出)结构,用于存储在代码执行期间创建的所有执行上下文。
首次运行 JS 代码时,会创建一个全局执行上下文并 Push 到当前的执行栈中。每当发生函数调用,引擎都会为该函数创建一个新的函数执行上下文并 Push 到当前执行栈的栈顶
根据执行栈 LIFO 规则,当栈顶函数运行完成后,其对应的函数执行上下文将会从执行栈中 Pop 出,上下文控制权将移到当前执行栈的下一个执行上下文。
  • 当 Javascript 引擎遇到一段可执行代码时,新建一个执行上下文。
  • 将它推入执行栈中。并设置为运行时执行上下文。
  • 如果存在其他执行上下文。
  • 那么将当前执行上下文挂起
  • 然后再将新执行上下文推入执行栈中。
  • 执行上下文运行完毕,弹出销毁恢复并将原执行上下文设为运行时。

任务队列(Task Queue)

根据规范,事件循环是通过任务队列的机制来进行协调的。一个 Event Loop 中,可以有一个或者多个任务队列(task queue),一个任务队列便是一系列有序任务(task)的集合;每个任务都有一个任务源(task source),源自同一个任务源的 task 必须放到同一个任务队列,从不同源来的则被添加到不同队列。setTimeout/Promise 等 API 便是任务源,而进入任务队列的是他们指定的具体执行任务。
在事件循环中,每进行一次循环操作称为 tick,每一次 tick 的任务处理模型是比较复杂的,但关键步骤如下:
  • 在此次 tick 中选择最先进入队列的任务(oldest task),如果有则执行(一次)
  • 检查是否存在 微任务(Microtasks),如果存在则不停地执行,直至清空
  • 更新 render
  • 主线程重复执行上述步骤
在上述 tick 的基础上需要了解以下几点:
  • JS 分为同步任务和异步任务
  • 同步任务都在主线程上执行,形成一个执行栈
  • 主线程之外,事件触发线程管理着一个任务队列,只要异步任务有了运行结果,就在任务队列之中放置一个事件。
  • 一旦执行栈中的所有同步任务执行完毕(此时 JS 引擎空闲),系统就会读取任务队列,将可运行的异步任务添加到可执行栈中,开始执行
同步任务
同步任务是指在主线程上排队执行的任务,只有前一个任务执行完毕,才能继续执行下一个任务,当我们打开网站时,网站的渲染过程,比如元素的渲染,其实就是一个同步任务
异步任务
异步任务是指不进入主线程,而进入任务队列的任务,只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程,当我们打开网站时,像图片的加载,音乐的加载,其实就是一个异步任务
常见异步任务
  • ajax 的回调函数
  • DOM 事件的回调函数
  • setTimeout、setInterval 定时器的回调函数

四、事件循环机制

在这里插入图片描述

  • JS 线程在同一时间只执行一个任务,期间可能创建多个函数执行环境,对应 Frame。
  • 在执行任务的时候,随时执行环境栈的动态变化,相对应的变量对象不断创建销毁。对应 Object。
  • 异步任务 ajax I/O 等得到结果时,会将其回调作为一个任务添加到任务队列,排队等待执行
  • 当 JS 线程中的任务执行完毕,会读取任务队列 Queue,并将队列中的第一个任务添加到 JS 线程中并执行。
  • 循环 3 4 步,异步任务完成后不断地往任务队列中添加任务,线程空闲时从任务列表读取任务并执行。
事件循环下的宏任务与微任务
通常我们把异步任务分为宏任务与微任务,它们的区分在于:
  • 宏任务(macro-task):一般是 JS 引擎和宿主环境发生通信产生的回调任务,比如 setTimeout,setInterval 是浏览器进行计时的,其中回调函数的执行时间需要浏览器通知到 JS 引擎,网络模块, I/O 处理的通信回调也是。包含有 setTimeout,setInterval,DOM 事件回调,ajax 请求结束后的回调,整体 script 代码,setImmediate。
  • 微任务(micro-task):一般是宏任务在线程中执行时产生的回调,如 Promise.then() .catch()(本身是同步任务),process.nextTick(node),Object.observe(已废弃), MutationObserver(DOM 监听)async/await,这些都是 JS 引擎自身可以监听到回调
优先级
  • 宏任务优先级,主代码块 > setImmediate > MessageChannel > setTimeout / setInterval
  • 微任务优先级,process.nextTick > Promise > MutationObserver
    在这里插入图片描述
运行机制
在事件循环中,每进行一次循环操作称为 tick,每一次 tick 的任务处理模型是比较复杂的,但关键步骤如下:

在这里插入图片描述

五、浏览器环境下的事件循环机制

常见宏任务
  • script(整体代码)
  • setTimeout
  • setInterval
  • I/O
  • 网络请求
  • UI 交互事件(HTML解析、鼠标事件、键盘事件)
  • postMessage
  • MessageChannel
常见微任务
  • Promise.then() .catch()
  • Object.observe
  • MutationObserver
microtask,可以理解是在当前 task 执行结束后立即执行的任务。也就是说,在当前 task 任务后,下一个 task 之前,在渲染之前。
所以它的响应速度相比 setTimeout(setTimeout 是 task)会更快,因为无需等渲染。也就是说,在某一个宏任务 (macrotask) 执行完后,就会将在它执行期间产生的所有微任务 (microtask) 都执行完毕(在渲染前)。
setTimeout(() => {//执行后 回调一个宏事件console.log('内层宏事件3')
}, 0)
console.log('外层宏事件1');new Promise((resolve) => {console.log('外层宏事件2');resolve()
}).then(() => {console.log('微事件1');
}).then(()=>{console.log('微事件2')
})
外层宏事件1
外层宏事件2
微事件1
微事件2
内层宏事件3

这篇关于事件循环的学习、执行上文、this、执行栈和任务队列的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/297689

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

好题——hdu2522(小数问题:求1/n的第一个循环节)

好喜欢这题,第一次做小数问题,一开始真心没思路,然后参考了网上的一些资料。 知识点***********************************无限不循环小数即无理数,不能写作两整数之比*****************************(一开始没想到,小学没学好) 此题1/n肯定是一个有限循环小数,了解这些后就能做此题了。 按照除法的机制,用一个函数表示出来就可以了,代码如下

hdu1180(广搜+优先队列)

此题要求最少到达目标点T的最短时间,所以我选择了广度优先搜索,并且要用到优先队列。 另外此题注意点较多,比如说可以在某个点停留,我wa了好多两次,就是因为忽略了这一点,然后参考了大神的思想,然后经过反复修改才AC的 这是我的代码 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<

禁止平板,iPad长按弹出默认菜单事件

通过监控按下抬起时间差来禁止弹出事件,把以下代码写在要禁止的页面的页面加载事件里面即可     var date;document.addEventListener('touchstart', event => {date = new Date().getTime();});document.addEventListener('touchend', event => {if (new

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学