javascript技巧

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript技巧 > JavaScript事件循环

简单聊聊JavaScript中的事件循环

作者:mick

js的事件循环(event-loop)是我们前端学习中非常重要的一部分,也是面试中经常被问到的点,这篇文章我们就来给大家着重讲解一下吧

为什么js是单线程的

我们首先要考虑下js作为浏览器脚本语言,主要用途就是和用户互动和操作DOM。比如js同时有两个线程,两个线程同时操作同一个DOM,比如一个给DOM添加内容,一个移除DOM,那到底该听谁的呢?所以这就决定了它只能是单线程,否则就会出现一些奇怪的问题。

浏览器

我们每打开一个tab页就会产生一个进程

浏览器都包含哪些进程呢

浏览器进程

第三方插件进程

每种类型的插件对应一个进程,当使用该插件时才创建。因插件易崩溃,所以需要通过插件进程来隔离,以保证插件进程崩溃不会对浏览器和页面造成影响

GPU进程

该进程只有一个,用于3D绘制等

渲染进程

网络进程

主要负责页面的网络资源加载。

如果浏览器是单进程,那么当一个tab页面崩溃了,就会影响到整个浏览器。同时如果插件崩溃了也会影响整个浏览器。浏览器进程有很多,每个进程又有很多的线程,都会占用内存。进程之间的内容相互隔离,这是为了保护操作系统中进行互不干扰的技术,每一个进程只能访问自己占有的数据,也就避免了进程A写入数据到进程B的情况。因为进程之间的数据是严格隔离的,所以一个进程如果崩溃了,或者挂起了,是不会影响到其他进程的。

渲染进程

页面的渲染、js的执行、事件的循环、都在渲染进程中执行,所以重点看下渲染进程。渲染进程是多线程的,下面看下比较常用的几个线程

GUI线程

负责渲染浏览器界面,解析HTML,CSS,构建DOM树和RenderObject树,布局和绘制等。

当修改了一些元素的颜色或者背景色,页面就会重绘(Repaint)

当修改元素的尺寸,页面就是重排也叫回流(Reflow)

当页面需要重绘和重排的时候GUI线程执行,绘制页面

重绘和重排的成本比较高,尽量避免重绘和重排

GUI线程和JS引擎线程是互斥的

JS引擎线程

JS引擎线程就是JS内核,负责处理JavaScript脚本程序(例如V8引擎)

JS引擎线程负责解析JavaScript脚本,运行代码

JS引擎一直等待任务队列的到来,然后加以处理

GUI线程和JS引擎是互斥的,JS引擎线程会阻塞GUI渲染线程

如果JS执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞。

事件触发线程

定时触发器线程

异步HTTP请求线程

下面就来谈谈我们的重头戏

事件循环

let setTimeoutCallBack = function() {
  console.log('我是定时器回调');
};
let httpCallback = function() {
  console.log('我是http请求回调');
}

// 同步任务
console.log('我是同步任务1');

// 异步定时任务
setTimeout(setTimeoutCallBack,1000);

// 异步http请求任务
ajax.get('/info',httpCallback);

// 同步任务
console.log('我是同步任务2');

我们来看下这段代码。解析一下执行过程

浏览器上的所有线程是的行为都很单一。

了解了事件循环下面看下宏任务和微任务

宏任务 微任务

宏任务

为了协调这些任务能够有序的在主线程上执行,页面进行引入了消息队列和事件循环机制,渲染进程内部会维护多个消息队列,主线程不断的从这些任务队列中取出任务并执行任务。我们把这些消息队列中的任务称为宏任务

常见的宏任务:

微任务

微任务就是一个需要异步执行的函数,执行时机是在主函数执行结束之后、当前宏任务结束后之前。

异步回调有两种方式

我们知道宏任务结束后,会执行渲染,然后执行下一次宏任务,微任务可以理解为当前宏任务执行后立即执行的任务。

常见的微任务:

当执行完一个宏任务之后,会立即执行期间所产生的所有微任务,然后执行渲染

宏任务微任务的执行流程

浏览器首先会执行一个宏任务,然后执行当前执行栈所产生的微任务,然后再渲染页面,然后再执行下一个宏任务

面试题

function test() {
  console.log(1)
  setTimeout(function () { 	// timer1
    console.log(2)
  }, 1000)
}

test();

setTimeout(function () { 		// timer2
  console.log(3)
})

new Promise(function (resolve) {
  console.log(4)
  setTimeout(function () { 	// timer3
    console.log(5)
  }, 100)
  resolve()
}).then(function () {
  setTimeout(function () { 	// timer4
    console.log(6)
  }, 0)
  console.log(7)
})

console.log(8)

下面我们来分析一下整体的流程

首先应该找到同步任务先执行

我们再看异步任务

到此这篇关于简单聊聊JavaScript中的事件循环的文章就介绍到这了,更多相关JavaScript事件循环内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文