javascript 是单线程 ?

javascript 是单线程的吗?


前言

如果别人问你javascript 是单线程的吗?你会怎样回答?


实际上这个说法是有小错的。严谨一点来说是:js引擎是单线程的。

一、浏览器的进程-线程

在这里插入图片描述

这里不讨论进程线程的概念。

1. 浏览器是多进程的

Browser进程:

浏览器的主进程(负责协调、主控),只有一个。

  • 负责浏览器界面显示,与用户交互。如前进,后退等
  • 负责各个页面的管理,创建和销毁其他进程
  • 将Renderer进程得到的内存中的Bitmap,绘制到用户界面上
  • 网络资源的管理,下载等

浏览器渲染进程

  • 浏览器内核
  • Renderer进程,内部是多线程的
  • Chrome会尽可能为每一个tab甚至是页面里面的每一个iframe都分配一个单独的进程。
  • 作用:页面渲染,脚本执行,事件处理

二、渲染进程的线程

1. GUI渲染线程

作用:

  • 负责渲染浏览器界面,解析HTML,CSS,构建DOM树和RenderObject树,布局和绘制等。
  • 当界面需要重绘,回流(reflow)时,该线程就会执行。
  • GUI渲染线程与JS引擎线程是互斥的。

2. JS引擎线程

  • 也称为JS内核,负责处理Javascript脚本程序。(例如V8引擎)
  • JS引擎线程负责解析Javascript脚本,运行代码。
  • 一个Tab页(renderer进程)中无论什么时候都只有一个JS线程在运行JS程序(js是单线程的由来)

3. 事件触发线程

  • 归属于浏览器而不是JS引擎,用来控制事件循环(可以理解,JS引擎自己都忙不过来,需要浏览器另开线程协助)
  • 当JS引擎执行代码块如setTimeOut时(也可来自浏览器内核的其他线程,如鼠标点击、AJAX异步请求等),会将对应任务添加到事件线程中
  • 当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理

4. 定时触发器线程

  • setInterval与setTimeout所在线程
  • 浏览器定时计数器并不是由JavaScript引擎计数的,(因为JavaScript引擎是单线程的, 如果处于阻塞线程状态就会影响记计时的准确)

5. 异步http请求线程

  • 在XMLHttpRequest在连接后是通过浏览器新开一个线程请求将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件队列中。再由JavaScript引擎执行。

6. 额外 WebWorker与SharedWorker

这是新的api 我没有实际用过。
WebWorker:

  • 创建Worker时,JS引擎向浏览器申请开一个子线程(子线程是浏览器开的,完全受主线程控制,而且不能操作DOM)
  • JS引擎线程与worker线程间通过特定的方式通信(postMessage API,需要通过序列化对象来与线程交互特定的数据

SharedWorker:

  • 是浏览器所有页面共享的,不能采用与Worker同样的方式实现,因为它不隶属于某个Render进程,可以为多个Render进程共享使用
  • 所以Chrome浏览器为SharedWorker单独创建一个进程来运行JavaScript程序,在浏览器中每个相同的JavaScript只存在一个SharedWorker进程,不管它被创建多少次。

三、js运行机制

在这里插入图片描述

看这段代码:

console.log(1);
setTimeout(() => {
    console.log(2);
}, 1000)
console.log(3);

结果:1 3 2
如果按照js是单线程的结果应该是1 2 3

解释: js 引擎线程处理这段代码过程:

  • 打印1
  • 遇到setTimeout() 这是浏览器提供的webapi 其实就是window对象提供,node为global,让浏览器开定时触发器线程,处理setTimeout
  • js引擎继续执行 打印 3
  • 当定时触发器线程,时间到了,通知事件触发线程,把回调函数加入添加到待处理队列的队尾,当处理完执行栈中所有任务,执行刚才的回调,打印2

1. 事件循环Event Loop

在这里插入图片描述

简单点就是,js 分为同步任务和异步任务。

  • js 先顺序执行所有的同步任务,
  • 遇到异步任务由相应的web api 提供者(浏览器 或者其他宿主 ), 开其他线程执行异步任务,如ajax …这些其他线程完成后,把回调函数注册到事件队列中。
  • 当执行栈所有同步任务执行完成后,事件队列中循环按照先后推进执行栈中。一直循环。

所以为啥setTimeout 不准的原因知道了吧。

2. 宏任务与微任务

除了广义的同步任务和异步任务,我们对任务有更精细的定义:

  • macro-task(宏任务):包括整体代码script,setTimeout,setInterval
  • micro-task(微任务):Promise,process.nextTick

注意:node环境下,process.nextTick的优先级高于Promise
不同类型的任务会进入对应的Event Queue。

setTimeout(function() {
    console.log('setTimeout');
})

new Promise(function(resolve) {
    console.log('promise');
}).then(function() {
    console.log('then');
})

console.log('console');

promise
console
setTimeout

顺序是:

  • 先执行同步任务(整体代码script作为一个宏任务),把相应的异步宏任务,加入宏任务队列,微任务加入微任务队列。
  • 执行完所有同步任务,微任务队列入主执行栈。
    接下来步骤就是循环了
  • 去宏任务队列中执行宏任务,执行后去微任务队列有无事件,有执行。完成后去宏任务

其实把整体代码script(同步任务)作为一个宏任务,就好理解了。
先执行一个宏任务再执行所有微任务。
在这里插入图片描述

四、做道题

console.log('1');

setTimeout(function() {
    console.log('2');
    process.nextTick(function() {
        console.log('3');
    })
    new Promise(function(resolve) {
        console.log('4');
        resolve();
    }).then(function() {
        console.log('5')
    })
})
process.nextTick(function() {
    console.log('6');
})
new Promise(function(resolve) {
    console.log('7');
    resolve();
}).then(function() {
    console.log('8')
})

setTimeout(function() {
    console.log('9');
    process.nextTick(function() {
        console.log('10');
    })
    new Promise(function(resolve) {
        console.log('11');
        resolve();
    }).then(function() {
        console.log('12')
    })
})

注意:node环境运行
结果:1 7 6 8 2 4 3 5 9 11 10 12

总结

多点实践才是正途。有空研究一下css渲染过程。
参考文献/视频/博客:
https://juejin.cn/post/6844903553795014663
https://zhuanlan.zhihu.com/p/102149546
https://www.bilibili.com/video/BV16q4y1o7EG?p=18
https://juejin.cn/post/6844903512845860872

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值