与JavaScript异步实现密切相关的浏览器内核线程

JavaScript是单线程的,但是浏览器内核是多线程的。这个是JavaScript异步实现的关键之处。

关于Ajax

JavaScript中关于Ajax的调用方式有两种:同步和异步。相信看到这篇文章的时候,你已经产生了这样的疑问了:为什么同步是阻塞UI的,为什么异步就不会阻塞UI了,异步又是怎么实现的呢?
那为什么要区分这两种情况呢?这和浏览器处理机制是密不可分的。JavaScript虽然是单线程的,但是浏览器内核是多线程的。在日常开发中尽管不会有线程间通信,但实际上JS的宿主环境在后台已经帮我们处理好了。JavaScript的异步实现实际上就是线程间的通信问题。

浏览器内核中的多线程

先来了解一下浏览器内核中都有哪些线程,以及这些线程是用来干什么的。

浏览器 GUI 渲染线程

面试中常常提及的一个问题就是页面渲染的执行过程,这里其实就是GUI线程在运行。

  • 负责渲染浏览器界面,包括解析HTML、CSS、构建DOM树、Render树、布局与绘制等
  • 当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行

当浏览器中JavaScript引擎线程在执行过程中触发UI渲染的时候,GUI线程此时是冻结状态。也就是说对页面结构的改动并不会立即渲染到页面上,根本原因是GUI线程和JavaScript线程互斥。因为 JavaScript脚本是可操纵DOM元素, 在修改这些元素属性同时渲染界面, 那么渲染线程前后获得的元素数据就可能不一致了。因此浏览器决定将这两个线程互斥处理。所以在脚本中执行对界面进行更新操作, 如添加结点,删除结点或改变结点的外观等更新并不会立即体现出来, 这些操作将保存在一个队列中,待JavaScript引擎空闲时才有机会渲染出来。(JavaScript执行栈为空?)

可以点开console查看下面代码的运行效果,对页面的修改将会延迟处理。

var sleep = function(time) {
    var date = new Date(); 
    while(new Date() - date <= time) {}
}
document.body.innerHTML = '123';// reflow -- js运行过程中GUI处于冻结状态,对页面的修改不会立即执行,而是压入GUI线程的事件队列中等待执行。
sleep(3000)

所以当遇到在js引擎运算量较大的时候, 又想及时调用GUI渲染怎么办呢?参考下面的代码:

var sleep = function(time) {
    var date = new Date(); 
    while(new Date() - date <= time) {}
}
document.body.innerHTML = '123';
setTimeout(function() {//划重点 -- 定时器是浏览器的另一个线程,所以这里js的执行栈暂时是空的,也就是说不会影响到上一行代码触发的GUI线程。
   sleep(3000);
}, 0);

JavaScript 引擎线程

  • JavaScript 事件驱动机制,事件循环和事件队列。
  • 是JavaScript 代码的执行线程。
  • 所有在其他线程中等待压入事件队列的事件都将在这个线程中执行,也就是JavaScript中所谓的单线程处理机制。

浏览器定时触发器线程

  • setInterval与setTimeout所在线程。
  • 定时计时器并不是由JS引擎计时的,因为如果JS引擎是单线程的,如果JS引擎处于堵塞状态,那会影响到计时的准确。
  • 当计时完成被触发,事件会被添加到事件队列,等待JS引擎空闲了执行。而setInterval就是在一定的事件间隔内不断的将这个事件压入事件队列。当setInterval中的事件处理时间大于延迟时间时会不断重复触发事件处理。
    注意:W3C的HTML标准中规定,setTimeout中低于4ms的时间间隔算为4ms。

浏览器事件触发线程

  • 听起来像JS的执行,但是其实归属于浏览器,而不是JS引擎,用来控制时间循环(可以理解,JS引擎自己都忙不过来,需要浏览器另开线程协助)
  • 当JS引擎执行代码块如setTimeout时(也可来自浏览器内核的其他线程,如鼠标点击、AJAX异步请求等),会将对应任务添加到事件线程中
  • 当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理
    注意:由于JS的单线程关系,所以这些待处理队列中的事件都得排队等待JS引擎处理(当JS引擎空闲时才会去执行)

浏览器 http 异步请求线程

  • 在XMLHttpRequest在设置async为true时连接后新启动的一个线程
  • 线程如果检测到请求的状态变更,如果设置有回调函数,该线程会把回调函数添加到事件队列。

Ajax同步更新UI

从上面的线程中来看,JavaScript中调用的Ajax实际上是启动的另一个线程进行IO或者通信等耗时操作,一旦设置成同步,当然作为JavaScript引擎线程自然也可以进行网络通信,当耗时较长时,由于GUI线程处于冻结状态,浏览器将暂时的失去响应陷入卡死状态。
如果项目需求依旧是需要同步处理,那么合理避免GUI线程和JavaScript线程的互斥的方法就可以是调用浏览器的定时线程了

setTimeOut(function(){
//ajax..
},0)//通过定时线程立即压入js线程中的事件队列

感谢巨人的肩膀:

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值