一 、进程与线程

1.浏览器是多进程的

它主要包括以下进程:

  • Browser进程:浏览器的主进程,唯一,负责创建和销毁其它进程、网络资源的下载与管理、浏览器界面的展示、前后进退等。
  • GPU进程:用于3D绘制等,最多一个。
  • 第三方插件进程:每种类型的插件对应一个进程,仅当使用该插件时才创建。
  • 浏览器渲染进程(浏览器内核):内部是多进程的,每打开一个网页就会创建一个进程。主要用于页面渲染,脚本执行、事件处理等。

    2.渲染进程(浏览器内核)

    浏览器的渲染进程是多线程的,页面的渲染,javascript的执行、事件的循环都在这个进程内执行:

  • GUI渲染线程:负责渲染浏览器界面,当界面需要重绘(Repaint)或由于某种操作引发回流(Reflow)时,该线程就会执行。

  • javascript引擎线程:也称为javascript内核,负责处理javascript脚本程序、解析javascript脚本、运行代码等(例如v8引擎)
  • 事件触发线程:用来控制浏览器事件循环,注意这不归javascript引擎线程管,当事件被触发时,该线程会把事件的回调函数添加到待处理队列的队尾,等待javascript引擎的处理。
  • 定时触发器线程:传说中的setTimeout和setInterval所在线程,注意W3C在HTML标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms。
  • 异步http请求线程:在XMLHttpRequest连接后通过浏览器新开一个线程请求,将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件队列中。再由javascript引擎执行。

注意,GUI渲染线程与javascript引擎线程是互斥的,当javascript引擎执行时
GUI线程会被挂起(相当于被冻结了),GUI更新会被保存在一个队列中等到javascript引擎空闲时立即被执行。所以如果javascript执行时间过长,这样就会造成页面的渲染不连贯,导致页面加载阻塞。

二、单线程的javascript

所谓单线程,是指在javascript引擎中负责解释和执行javascritp代码的线程唯一,同一时间上只能执行一件任务。

问题:为什么要引入单线程

我们知道;

  • 浏览器需要渲染Dom
  • javascript可以更改Dom
  • javascritp执行时,浏览器Dom渲染停止

如果javascript引擎线程不是单线程的,那么可以同时执行多端javascript,如果这多段javascript都修改Dom,那么就会出现Dom冲突。

你可能会说 web worker 支持多线程,但是web worker不能访问window对象、document对象等。

原因:避免DOM渲染的冲突

当然,我们可以为浏览器引入的机制来解决这些冲突,但其大大提高了复杂性,所以javascript从诞生开始就选择了单线程执行。

引入单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务,这同时又导致一个问题,如果前一个任务耗时很长,后一个任务就不得不一直等着。

  1. // 实例1
  2. let i, sum = 0
  3. for(i = 0; i < 1000000000; i ++) {
  4. sum += i
  5. }
  6. console.log(sum)

在实例1中,sum并不能立刻打印出来,必须在for循环执行完之后才能执行console.log(sum)

  1. // 实例2
  2. console.log(1)
  3. alert('hello')
  4. console.log(2)

在实例2中,浏览器先打印1,然后弹出弹框点击确定后才执行console.log(2)

总结:

  • 优点:实现比较简单,执行环境相对单纯。
  • 缺点:只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。常见的浏览器无响应(假死),往往就是因为某一段javascript代码长时间运行(比如死循环),导致整个页面卡在这个地方,其它任务无法执行。

为了 解决这个问题,javascript语言将任务的执行模式分为两种:同步和异步。