亲宝软件园·资讯

展开

简单聊聊JavaScript中的事件循环

mick 人气:0

为什么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)

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

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

我们再看异步任务

加载全部内容

相关教程
猜你喜欢
用户评论