[浏览器事件循环] javaScript事件循环 EventLoop
前言
Event Loop即事件循环,是指浏览器或Node的一种解决javaScript单线程运行时不会阻塞的一种机制,也就是我们经常使用异步的原理。
先熟悉基本概念
【堆Heap】
堆是一种数据结构,是利用完全二叉树维护的一组数据,堆分为两种,一种为最大堆,一种为最小堆,将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
堆是线性数据结构,相当于一维数组,有唯一后继。
【栈Stack】
栈在计算机科学中是限定仅在表尾进行插入或删除操作的线性表。 栈是一种数据结构,它按照后进先出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据。
栈是只能在某一端插入和删除的特殊线性表。
【队列Queue】
特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。
进行插入操作的端称为队尾,进行删除操作的端称为队头。 队列中没有元素时,称为空队列。
【进程】
进程是系统分配的独立资源,是 CPU 资源分配的基本单位,进程是由一个或者多个线程组成的。
【线程】
线程是进程的执行流,是CPU调度和分派的基本单位,同个进程之中的多个线程之间是共享该进程的资源的。
把这些概念拿到浏览器中来说,当你打开一个 Tab 页时,其实就是创建了一个进程,一个进程中可以有多个线程,比如渲染线程、JS 引擎线程、HTTP 请求线程等等。当你发起一个请求时,其实就是创建了一个线程,当请求结束后,该线程可能就会被销毁。
浏览器内核
浏览器是多进程的,浏览器每一个 tab 标签都代表一个独立的进程(也不一定,因为多个空白 tab 标签会合并成一个进程),浏览器内核(浏览器渲染进程)属于浏览器多进程中的一种。
浏览器内核有多种线程在工作。
【GUI 渲染线程】
负责渲染页面,解析 HTML,CSS 构成 DOM 树等,当页面重绘或者由于某种操作引起回流都会调起该线程。
和 JS 引擎线程是互斥的,当 JS 引擎线程在工作的时候,GUI 渲染线程会被挂起,GUI 更新被放入在 JS 任务队列中,等待 JS 引擎线程空闲的时候继续执行。
【JS 引擎线程】
单线程工作,负责解析运行 JavaScript 脚本。
和 GUI 渲染线程互斥,JS 运行耗时过长就会导致页面阻塞。
【事件触发线程】
当事件符合触发条件被触发时,该线程会把对应的事件回调函数添加到任务队列的队尾,等待 JS 引擎处理。
【定时器触发线程】
浏览器定时计数器并不是由 JS 引擎计数的,阻塞会导致计时不准确。
开启定时器触发线程来计时并触发计时,计时完成后会被添加到任务队列中,等待 JS 引擎处理。
【http 请求线程】
http 请求的时候会开启一条请求线程。
请求完成有结果了之后,将请求的回调函数添加到任务队列中,等待 JS 引擎处理。
基础知识我们基本了解了些必要的,下面我们开始介绍Event Loop
js中的任务分类
任务被分为两种,一种宏任务(MacroTask)也叫Task,一种叫微任务(MicroTask) 也叫jobs
【MacroTask(宏任务)】
类型:script全部代码、setTimeout、setInterval、setImmediate、I/O、UI Rendering
【MicroTask(微任务)】
类型:Process.nextTick(Node独有)、Promise 、MutationObserver
Event Loop
目前讨论的两种情况:浏览器的Event Loop 以及Node中的Event Loop
浏览器中的Event Loop
Javascript 有一个 main thread 主线程和 call-stack 调用栈(执行栈),所有的任务都会被放到调用栈等待主线程执行。
【JS调用栈】
JS调用栈采用的是后进先出的规则,当函数执行的时候,会被添加到栈的顶部,当执行栈执行完成后,就会从栈顶移出,直到栈内被清空。
【同步任务和异步任务】
Javascript单线程任务被分为同步任务和异步任务,同步任务会在调用栈中按照顺序等待主线程依次执行,异步任务会在异步任务有了结果后,将注册的回调函数放入任务队列中等待主线程空闲的时候(调用栈被清空),被读取到栈内等待主线程的执行。
浏览器进行事件循环工作方式
1、选择当前要执行的任务队列,选择任务队列中最先进入的任务,如果任务队列为空即null,则执行跳转到微任务(MicroTask)的执行步骤。
2、将事件循环中的任务设置为已选择任务。
3、执行任务。
4、将事件循环中当前运行任务设置为null。
5、将已经运行完成的任务从任务队列中删除。
6、microtasks步骤:进入microtask检查点。
7、更新界面渲染。
8、返回第一步。
【执行进入microtask检查点时,浏览器会执行以下步骤:】
设置microtask检查点标志为true。
当事件循环microtask执行不为空时:选择一个最先进入的microtask队列的microtask,将事件循环的microtask设置为已选择的microtask,运行microtask,将已经执行完成的microtask为null,移出microtask中的microtask。
清理IndexDB事务
设置进入microtask检查点的标志为false。
【重点】
总结以上规则为一条通俗好理解的:
1、顺序执行先执行同步方法,碰到MacroTask直接执行,并且把回调函数放入MacroTask执行队列中(下次事件循环执行);碰到microtask直接执行。把回调函数放入microtask执行队列中(本次事件循环执行)
2、当同步任务执行完毕后,去执行微任务microtask。(microtask队列清空)
3、由此进入下一轮事件循环:执行宏任务 MacroTask (setTimeout,setInterval,callback)
[总结]所有的异步都是为了按照一定的规则转换为同步方式执行。
查看一个示例
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
Promise.resolve().then(function() {
console.log('promise1');
}).then(function() {
console.log('promise2');
});
console.log('script end');
1、一开始task队列中只有script,则script中所有函数放入函数执行栈执行,代码按顺序执行。
2、接着遇到了setTimeout,它的作用是0ms后将回调函数放入task队列中,也就是说这个函数将在下一个事件循环中执行(注意这时候setTimeout执行完毕就返回了)。
3、接着遇到了Promise,按照前面所述Promise属于microtask,所以第一个.then()会放入microtask队列。
4、当所有script代码执行完毕后,此时函数执行栈为空。
5、开始检查microtask队列,此时队列不为空,执行.then()的回调函数输出'promise1',由于.then()返回的依然是promise,所以第二个.then()会放入microtask队列继续执行,输出'promise2'。此时microtask队列为空了,
6、进入下一个事件循环,检查task队列发现了setTimeout的回调函数,立即执行回调函数输出'setTimeout',代码执行完毕。
小结
基本上能理解这个例子的话,对于浏览器的事件循环应该已经可以理解的差不多了。由于本篇文章涉及的知识点比较多,不易篇幅太长,至于node的事件循环方式则跟浏览器的实现方式不太一样。所以后面会在总结一篇文章
[浏览器事件循环] javaScript事件循环 EventLoop的更多相关文章
- 【转载】浅谈事件冒泡与事件捕获 - javascript 事件代理
原文:https://segmentfault.com/a/1190000000749838 事件冒泡与事件捕获 事件冒泡和事件捕获分别由微软和网景公司提出,这两个概念都是为了解决页面中事件流(事件发 ...
- jQuery事件和JavaScript事件
1.JavaScript事件: 属性 当以下情况发生时,出现此事件 FF N IE onabort 图像加载被中断 1 3 4 onblur 元素失去焦点 1 2 3 onchange 用户改变域的内 ...
- 浏览器中的JavaScript事件循环机制
浏览器的事件循环机制是HTML中定义的规范. JavaScript有一个主线程和调用栈,所有的任务都会被放到调用栈等待主线程执行. JS调用栈 是一种先进后出的数据结构.当函数被调用时,会被添加到栈中 ...
- javaScript事件(六)事件类型之滚轮事件
滚轮事件其实就是一个mousewheel事件,这个事件跟踪鼠标滚轮,类似Mac的触屏版. 一.客户区坐标位置 鼠标事件都是在浏览器视口的特定位置上发生的.这个位置信息保存在事件对象的clientX和c ...
- Javascript事件模型(三):JavaScript事件绑定方法总结(及Jquery)
JavaScript中绑定事件的方法主要有三种: 1 在DOM元素中直接绑定 2 JavaScript代码中直接绑定 3 绑定事件监听函数 JQuery中绑定事件的几种方法 主要有on().bind( ...
- javaScript事件(八)事件类型之变动事件
DOM2级的变动(mutation)事件能在DOM中某一部分发送变化时给出提示.变动事件为XML或HTML DOM设计的,并不特定于某种语言.DOM2级定义了如下变动事件. DOMSubtreeMod ...
- javaScript事件(七)事件类型之键盘与文本事件
键盘事件如下: keydown:当用户按下键盘上的任意键时触发,而且如果按住不放的话,会重复触发此事件. keypress:当用户按下键盘上的字符键时触发,而且如果按住不放的话,会重复触发此事件. k ...
- js实例分析JavaScript中的事件委托和事件绑定
我们在学习JavaScript中,难免都会去网上查一些资料.也许偶尔就会遇到“事件委托”(也有的称我“事件代理”,这里不评论谁是谁非.以下全部称为“事件委托”),尤其是在查JavaScript的事件处 ...
- JavaScript的事件概述以及事件对象,事件流
事件处理程序 JavaScript 事件对象是由访问 Web 页面的用户引起的一系列操作,例如:用户点击页面上的某个按钮或者鼠标移动到页面的某个图片上而产生一系列的互动的反馈. 我们通过为指定事件绑定 ...
随机推荐
- Django REST framework基础:解析器和渲染器
解析器 解析器的作用 解析器的作用就是服务端接收客户端传过来的数据,把数据解析成自己可以处理的数据.本质就是对请求体中的数据进行解析. 在了解解析器之前,我们要先知道Accept以及ContentTy ...
- 第二节 pandas 基础知识
pandas 两种数据结构 Series和DataFrame 一 Series 一种类似与一维数组的对象 values:一组数据(ndarray类型) index:相关的数据索引标签 1.1 se ...
- 做自己的docker镜像(基于ubuntu:16.04)
基于ubuntu:16.04 apt-get update -y apt-get install sudo -y 换源 sudo apt-get install vim sudo vim /etc/a ...
- Linux:Day12(下) 进程、任务计划
vmstat命令: vmstat [options] [delay [ count]] procs: r:等待运行的进程的个数: b:处于不可中断睡眠态的进程个数:(被阻塞的队列的长度): memor ...
- 通过C#学Proto.Actor模型》之Remote
Proto.Actor中提供了基于tcp/ip的通迅来实现Remote,可以通过其Remot实现对Actor的调用. 先来看一个极简单片的远程调用. 码友看码: 引用NuGet包 Proto.Acto ...
- 调用Runtime.getruntime 下的exec方法时,有",<,|时该怎么办?
今天写一个用到编译的程序,遇到了问题. 在调用runtime.exec("javac HelloWorld.java");运行完美,也就是有生成.class. 而到了runtime ...
- HTML60秒倒计时
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 爬虫基础(一)-----request模块的使用
---------------------------------------------------摆脱穷人思维 <一> : 建立时间价值的概念,减少做那些"时间花的多收 ...
- opentack-openstack组件及功能(1)
一. OpenStack各组件间的关系 图22.1 OpenStack各组件间的关系 1.基础管理服务包含Keystone,Glance,Nova,Neutron,Horizon五个服务 (1)Key ...
- python 支付宝SDK
python 支付宝SDK代码如下 from datetime import datetime from Crypto.PublicKey import RSA from Crypto.Signatu ...