浏览器中的Event Loop(事件循环)机制

推荐提前阅读文章: 事件循环规范

EventLoop 简介

JavaScript是一门单线程的语言。单线程是指JavaScript在运行阶段(注意,是在运行阶段)一直在单个栈中执行。

  • 这个栈被称为JavaScript栈,因为闭包的存在,该栈和OS栈不能合并。

  • JS栈中栈帧和OS的栈帧有很大的差异,JS的栈帧保存的是引用,引用的对象中包含变量,OS栈的栈帧中储存的是变量,所以合并起来比较困难。

  • 多个线程同时操作DOM会产生并发,无疑会增加操作DOM的难度。

虽然单个线程在运行JavaScript,但是JavaScript是一门非阻塞的语言。

之所以设计为非阻塞的语言,是因为JavaScript在设计的时候为了适应用户在不同环境下和浏览器交互。
例如:用户上传文件,读取文件等。

非阻塞是指JavaScript遇到阻塞调用的时候不会等待结果的返回,立刻执行栈中的下一步。

JavaScript还可以执行异步操作。

非阻塞和异步是截然不同的两个概念:非阻塞针对单线程,异步针对多线程。

接下来我们通过一个Demo来描述非阻塞和异步之间的关系。

setTimeout(() => console.log('a'));
console.log('b')

上例中首先输出b,然后输出a。

过程描述:

  • JavaScript线程调用setTimeout函数。

除了setTimeout函数JavaScript还有很多异步API,例如:readFile、XMLHTTPRequest等。这些API是由JavaSript平台提供的。我们平时写的promise等异步函数归根结底也要依靠平台提供的异步API实现异步操作。用户不能自定义异步API。

  • 调用了原生的异步API之后,由V8引擎将异步API翻译给JavaScript平台,调用平台对应的API。控制权一直在该JavaScrip主线程中,继续向下执行代码,就像从未调用异步API一样。

JavaScript代码的运行一直是在JavaScript栈中进行的,该栈是单线程栈。

  • 异步操作执行完毕之后,平台会在事件队列中添加一个任务,每个线程都有自己的队列,异步操作的结果通过队列发回主线程。任务中有关于调用的元信息,还有主线程中回调函数的引用。

  • 主线程的调用堆栈清空后,平台将检查事件队列中有没有待处理的任务。如果有等待处理的任务,平台将着手处理,触发一个函数调用,把控制权返回给主线程中的那个函数。调用那个函数之后,如果主线程中的栈又变空了,平台再次检查事件队列中有没有可以处理的任务,这个循环会一直运转下去,直到调用堆栈和事件队列都为空,而且所有原生的异步API调用都已结束。

浏览器中的Event Loop(事件循环)机制_第1张图片

非阻塞是在单线程中而言,异步是在多线程中而言。异步+非阻塞机制帮助JavaScript完成高并发。

Browser EventLoop(重绘,重排,composite竟然是这样~)

首先推荐大家阅读这篇文章,了解重绘,重排,合成。

规范中,EventLoop过程中包括渲染事件,详情参考本文,下面展开详细描述。

一般在浏览器中rendering被划分为以下几个阶段:

在这里插入图片描述
但是实际上:

  • 每个厂商有自己的规定过程。例如在chrome中render过程包括但不限于下面的阶段:

浏览器中的Event Loop(事件循环)机制_第2张图片

  • 规范中只是提供了定义,但是没有提供实现。所以实际情况中每家浏览器厂商都有自己渲染过程的内部实现。

task分解

参考事件循环规范理解在EventLoop中什么是task。

下文是根据事件循环规范和NodeJS事件循环给出的浏览器事件循环机制的猜测。一定不会百分百正确。

前端编程过程

前端工程师在编写网页的时候通过标签将代码包括起来,代码一般编辑如下:

DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Documenttitle>
  head>
  <body>body>
html>

当需要与网页互动的时候,我们需要引入

你可能感兴趣的:(javascript)