浏览器事件循环机制(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代码是自上向下执行的,在主线程中立即执行的就 ...
随机推荐
- 分布式session一致性问题
1.分布式session一致性 :指服务器集群情况下session共享的问题. 2.session的作用:保存服务器(tomcat)与客户端(浏览器)整个通讯的会话基本信息. 3.session应用场 ...
- MySQL - 性能优化 & MySQL常见SQL错误用法(转载)
1. LIMIT 语句 分页查询是最常用的场景之一,但也通常也是最容易出问题的地方.比如: , ; 一般DBA想到的办法是在type, name, create_time字段上加组合索引.这样条件排序 ...
- luogu1018乘积最大--区间DP
题目链接 https://www.luogu.org/problemnew/show/P1018 分析 这道题套路跟山区建小学差不多,可以先去看看那篇题解 \(f[i][j]\)表示枚举到第\(i\) ...
- 一个SDL2.0程序的分析
//把图片加载到SDL_Texture SDL_Texture* loadTexture(const std::string &file, SDL_Renderer *ren){ ...
- ES6中map数据结构学习
在项目中遇到一个很恶心的需求,然后发现ES6中的map可以解决,所以简单学习了一下map. Javascript的Object本身就是键值对的数据结构,但实际上属性和值构成的是“字符串-值”对,属性只 ...
- 【python+beautifulsoup4】Beautifulsoup4
Beautiful soup将复杂HTML文档转换成一个复杂的属性结构,每个节点都是python对象,所有对象可归纳为4种Tag,NavigableString,BeautifulSoup,Comme ...
- Java虚拟机(JVM)知多少
本文大量参考:https://www.cnblogs.com/lfs2640666960/p/9297176.html 概述 JVM是JRE的一部分.它是一个虚构出来的计算机,是通过在实际的计算机上仿 ...
- VMware虚拟机与Linux Centos7下载及安装教程
1.CentOS下载CentOS是免费版,推荐在官网上直接下载,网址:https://www.centos.org/download/ DVD ISO:普通光盘完整安装版镜像,可离线安装到计算机硬盘上 ...
- 从0到1写rtos:事件的控制块与通知
任务的状态: 未创建:只定义了任务代码,未调用tTaskInit()初始化 就绪:任务已经创建完毕,且等待机会占用CPU运行 运行:任务正在占用CPU运行代码 延时:任务调用tTaskDelay()延 ...
- [Educational Codeforces Round 63 ] D. Beautiful Array (思维+DP)
Educational Codeforces Round 63 (Rated for Div. 2) D. Beautiful Array time limit per test 2 seconds ...