浏览器事件循环机制(event loop)
JS是单线程的
JS是单线程的,或者说只有一个主线程,也就是它一次只能执行一段代码。JS中其实是没有线程概念的,所谓的单线程也只是相对于多线程而言。JS的设计初衷就没有考虑这些,针对JS这种不具备并行任务处理的特性,我们称之为“单线程”。
虽然JS运行在浏览器中是单线程的,但是浏览器是事件驱动的(Event driven),浏览器中很多行为是异步(Asynchronized)的,会创建事件并放入执行队列中。浏览器中很多异步行为都是由浏览器新开一个线程去完成,一个浏览器至少实现三个常驻线程:
- JS引擎线程
- GUI渲染线程
- 事件触发线程
JS引擎
JavaScript引擎是一个专门处理JavaScript脚本的虚拟机,一般会附带在网页浏览器之中,比如最出名的就是Chrome浏览器的V8引擎,如下图所示,JS引擎主要有两个组件构成:
- 堆-内存分配发生的地方
- 栈-函数调用时会形一个个栈帧(frame)
执行栈
每一个函数执行的时候,都会生成新的execution context(执行上下文),执行上下文会包含一些当前函数的参数、局部变量之类的信息,它会被推入栈中, running execution context(正在执行的上下文)始终处于栈的顶部。当函数执行完后,它的执行上下文会从栈弹出。
举个简单的例子:
function bar() {
console.log('bar');
}
function foo() {
console.log('foo');
bar();
}
foo();
复制代码
执行过程中栈的变化:
event loop(事件循环)
Wikipedia这样定义:
"Event Loop是一个程序结构,用于等待和发送消息和事件。(a programming construct that waits for and dispatches events or messages in a program.)"
简单说,就是在程序中设置两个线程:一个负责程序本身的运行,称为"主线程";另一个负责主线程与其他进程(主要是各种I/O操作)的通信,被称为"Event Loop线程"(可以译为"消息线程")。
事件循环与任务队列
事件循环可以简单描述为:
- 函数入栈,当Stack中执行到异步任务的时候,就将他丢给WebAPIs,接着执行同步任务,直到Stack为空;
- 在此期间WebAPIs完成这个事件,把回调函数放入CallbackQueue中等待;
- 当执行栈为空时,Event Loop把Callback Queue中的一个任务放入Stack中,回到第1步。
- Event Loop是由javascript宿主环境(像浏览器)来实现的;
- WebAPIs是由C++实现的浏览器创建的线程,处理诸如DOM事件、http请求、定时器等异步事件;
- JavaScript 的并发模型基于"事件循环";
- Callback Queue(Event Queue 或者 Message Queue) 任务队列,存放异步任务的回调函数
接下来看一个异步函数执行的例子:
var start=new Date();
setTimeout(function cb(){
console.log("时间间隔:",new Date()-start+'ms');
},500);
while(new Date()-start<1000){};
复制代码
- main(Script) 函数入栈,start变量开始初始化
- setTimeout入栈,出栈,丢给WebAPIs,开始定时500ms;
- while循环入栈,开始阻塞1000ms;
- 500ms过后,WebAPIs把cb()放入任务队列,此时while循环还在栈中,cb()等待;
- 又过了500ms,while循环执行完毕从栈中弹出,main()弹出,此时栈为空,Event Loop,cb()进入栈,log()进栈,输出'时间间隔:1003ms',出栈,cb()出栈
宏任务(Macrotasks)和微任务(Microtasks)
其实我们上面所说的都是宏任务(Macrotasks),但是js中还有一种队列微任务(Microtasks)。
macro-task(Task):一个event loop有一个或者多个task队列。task任务源非常宽泛,比如ajax的onload,click事件,基本上我们经常绑定的各种事件都是task任务源,还有数据库操作(IndexedDB ),需要注意的是setTimeout、setInterval、setImmediate也是task任务源。总结来说task任务源:
- setTimeout
- setInterval
- setImmediate
- I/O
- UI rendering
micro-task(Job):microtask 队列和task 队列有些相似,都是先进先出的队列,由指定的任务源去提供任务,不同的是一个 event loop里只有一个microtask 队列。另外microtask执行时机和Macrotasks也有所差异
- process.nextTick
- promises
- Object.observe
- MutationObserver
那么Macrotasks和Microtasks有什么别区别呢
举个简单的例子,假设一个script标签的代码如下:
Promise.resolve().then(function promise1 () {
console.log('promise1');
})
setTimeout(function setTimeout1 (){
console.log('setTimeout1')
Promise.resolve().then(function promise2 () {
console.log('promise2');
})
}, 0)
setTimeout(function setTimeout2 (){
console.log('setTimeout2')
}, 0)
复制代码
运行过程:
script里的代码被列为一个task,放入task队列。
循环1:
- 【task队列:script ;microtask队列:】
- 从task队列中取出script任务,推入栈中执行。
- promise1列为microtask,setTimeout1列为task,setTimeout2列为task。
- 【task队列:setTimeout1 setTimeout2;microtask队列:promise1】
- script任务执行完毕,执行microtask checkpoint,取出microtask队列的promise1执行。
循环2:
*【task队列:setTimeout1 setTimeout2;microtask队列:】 4. 从task队列中取出setTimeout1,推入栈中执行,将promise2列为microtask。
- 【task队列:setTimeout2;microtask队列:promise2】
- 执行microtask checkpoint,取出microtask队列的promise2执行。
循环3:
- 【task队列:setTimeout2;microtask队列:】
- 从task队列中取出setTimeout2,推入栈中执行。 7.setTimeout2任务执行完毕,执行microtask checkpoint。
- 【task队列:;microtask队列:】
综上所说,每次event loop循环执行栈完成后,会继续执行完相应的microtask任务
event loop中的Update the rendering(更新渲染)
这是event loop中很重要部分,在第7步会进行Update the rendering(更新渲染),规范允许浏览器自己选择是否更新视图。也就是说可能不是每轮事件循环都去更新视图,只在有必要的时候才更新视图。
来源链接:https://juejin.im/post/5afbc62151882542af04112d
浏览器事件循环机制(event loop)的更多相关文章
- js高级-浏览器事件循环机制Event Loop
JavaScript 是队列的形式一个个执行的 同一时间只能执行一段代码,单线程的 (队列的数据结构) 浏览器是多线程的 JavaScript执行线程负责执行js代码 UI线程负责UI展示的 Jav ...
- 一篇文章图文并茂地带你轻松学完 JavaScript 事件循环机制(event loop)
JavaScript 事件循环机制 (event loop) 本篇文章已经默认你有了基础的 ES6 和 javascript语法 知识. 本篇文章比较细致,如果已经对同步异步,单线程等概念比较熟悉的读 ...
- js事件循环机制(Event Loop)
javascript从诞生之日起就是一门 单线程的 非阻塞的 脚本语言,单线程意味着,javascript代码在执行的任何时候,都只有一个主线程来处理所有的任务,非阻塞靠的就是 event lo ...
- 初探nodejs事件循环机制event loop
nodejs的特点 nodejs 具有事件驱动和非阻塞I/O的特点. 事件驱动是指nodejs把每一个任务当成事件来处理. 非阻塞I/O是指nodejs遇到I/O任务时,会从线程池调度单独的线程处理I ...
- 关于事件循环机制event loop
setTimeout(()=> { console.log('settimeout') },100) console.log('开始') console.log('结束') new Promis ...
- 深入理解JavaScript的事件循环(Event Loop)
一.什么是事件循环 JS的代码执行是基于一种事件循环的机制,之所以称作事件循环,MDN给出的解释为 因为它经常被用于类似如下的方式来实现 while (queue.waitForMessage()) ...
- Node.js 事件循环(Event Loop)介绍
Node.js 事件循环(Event Loop)介绍 JavaScript是一种单线程运行但又绝不会阻塞的语言,其实现非阻塞的关键是“事件循环”和“回调机制”.Node.js在JavaScript的基 ...
- JS浏览器事件循环机制
文章来自我的 github 博客,包括技术输出和学习笔记,欢迎star. 先来明白些概念性内容. 进程.线程 进程是系统分配的独立资源,是 CPU 资源分配的基本单位,进程是由一个或者多个线程组成的. ...
- js的事件循环(Event Loop)
(本文从掘金小册整理) 首先介绍一下几个概念 进程与线程 相信大家经常会听到 JS 是单线程执行的,但是你是否疑惑过什么是线程? 讲到线程,那么肯定也得说一下进程.本质上来说,两个名词都是 CPU 工 ...
- js 彻底搞懂事件循环机制 Event Loop
我们都知道javascript是单线程语言,就是因为单线程的特性,就不得不提js中的同步和异步 一.同步和异步 所谓单线程,无非就是同步队列和异步队列,js代码是自上向下执行的,在主线程中立即执行的就 ...
随机推荐
- Wannafly挑战赛2D Delete (最短路好题)
大意: 给定DAG, 给定点$S,T$, 每次询问给出点$x$, 求删除$x$后的$S->T$的最短路, 询问之间独立. 删除点$x$的最短路一定要经过一条边$(u,v)$, 满足$u$拓扑序在 ...
- 怎样设置cookie的到期时间
1. 使用Cookie的: Expires 属性. 它可以设置cookie的过期时间. 下面的代码表示id这条cookie的过期时间是2015年10月21日早上7点28分; Set-Cookie: i ...
- Django rest-framework框架-用户权限实例
简单实例: class MyPermission(object): ''' 权限控制类 ''' def has_permission(self,request,view): if request.us ...
- 【图像处理 】 一、OSTU分割法
图像中像素的灰度值小于阈值T的像素个数记作N0,像素灰度大于阈值T的像素个数记作N1,则有: 图像大小:M*N T为二值化的阈值: N0为灰度小于T的像素的个数,N0的平均灰度为μ0 N1 为灰度大于 ...
- Chrome开发者工具中Elements(元素)断点的用途
SAP Engagement Center UI的这个按钮会每秒钟刷新一次,显示页面已经打开了多长时间. 需求:需要找到哪行JavaScript代码不断刷新的按钮文字. 按照经验判断,这个文字肯定是一 ...
- Java Object对象中的wait,notify,notifyAll的理解
wait,notify,notifyAll 是定义在Object类的实例方法,用于控制线程状态,在线程协作时,大家都会用到notify()或者notifyAll()方法,其中wait与notify是j ...
- MyBatis 插入失败后爆出 500 ,如何捕获异常?
我们在使用 Mybatis 的时候,会出现以下场景 数据表里有一些字段被设置为了 不可为 null 但是我们的用户在提交表单的时候没有提交所需的 字段数据 然后 Mybatis 在数据库做操作的时候就 ...
- linux usb驱动记录(二)
三.usb设备的识别过程 在这里梳理一下上一篇博客中的内容:(这张图来自https://blog.csdn.net/lizuobin2/article/details/51931161) 上一篇博客刚 ...
- Fiddler抓包HTTPS捕捉旧版App
“现在可以公开的情报:简易操作Fiddler抓包可能” 任何App的更新都限于苹果开发者规定,有时为了上架不得已放弃一些真正实用的功能,比如视频音频的直接下载,脚本的直接导入,手机上IPA的直接安装等 ...
- Ubuntu系统---EasyECD安装记录
说明:因解决Ubuntu花屏和频繁死机的问题(后来证实本人的电脑显卡驱动有问题),手残毁坏了系统,需重装.之前从未装过系统,经过三天,反复折腾装了近十次的系统,现总结如下. 第一步:Windows 系 ...