Wonderjar's Blog

从单线程,事件循环聊到Web Worker

2018-04-25

Javascript为什么是单线程

Javascript设计之初是为浏览器端服务的,比如操作dom,多线程很容易导致对dom的更新不一致,比如两个线程都在修改同一个dom节点,所以所有的浏览器规定Javascript只能有一个主线程

单线程与Event Loop

1
2
3
4
5
6
let end = false
setTimeout(() => {
end = true
}, 1000)
while(!end){console.log('processing')}
console.log('end')

从这一段代码说起,console里永远都不会看到end
因为Javascript会一直执行主线程(也就是Call Stack)中的代码,当主线程中的代码执行完了,才会去Event Queue里去找活干
从Event Queue找活干的过程是一直loop这个Queue,从先进Queue的开始,然后看每个task是否已经符合执行的条件,这包括比如http request收到了返回值,setTimeout达到了时间间隔的要求,用户点击了有事件监听的button
//TODO 这里还要研究一下

setTimeout准时吗

setTimout因为要等待主线程空闲才会来检查,所以如果没有其他干扰,setTimout还是比较准时的,但一旦主线程的执行耗费了大量时间,setTimeout的回调可能比规定时间晚很多很多

Event Queue无限长吗

//TODO 待验证

为什么有Web Worker

既然规定Javascript是单线程,为什么还有Web Worker?
使用Web Worker确实是多线程,但只要能避免多线程引起的问题,就是一开始提的更新dom
Web Worker中你能有的东西:
You can use most standard JavaScript features inside a web worker, including:
Navigator
XMLHttpRequest
Array, Date, Math, and String
WindowTimers.setTimeout and WindowTimers.setInterval
The main thing you can’t do in a Worker is directly affect the parent page.
This includes manipulating the DOM and using that page’s objects.
You have to do it indirectly, by sending a message back to the main script via DedicatedWorkerGlobalScope.postMessage, then actioning the changes from there.
所以你只要间接地去改dom就不会引起问题啦

Web Worker会让事情变快吗?

不一定
我理解的Web Worker的基本目的,其实是规避主线程执行一段耗时极长的逻辑,因为这样会导致页面UI无响应和Event Loop的执行被大大推后
所以我们启动一个Web Worker,只是为了不让主线程被block
但如果我们启动多个Web Worker,并且能分配和协调他们的工作,那就是多线程编程了,这个对于能并行计算的任务,一定是会大大加快计算速度的(并行处理的好处大于维护线程的开销时)

Web Worker会创建自己的Event Loop?

答案是 是的

Inline/Embedded Worker

通常的Worker,需要借助一个js文件来初始化
不过也有一些办法,不用单独的js文件也能初始化Worker
比如,

  • new Worker(‘data:text/javascript;charset=US-ASCII,onmessage%20%3D%20function%20%28oEvent%29%20%7B%0A%09postMessage%28%7B%0A%09%09%22id%22%3A%20oEvent.data.id%2C%0A%09%09%22evaluated%22%3A%20eval%28oEvent.data.code%29%0A%09%7D%29%3B%0A%7D’);
  • new Blob(Array.prototype.map.call(document.querySelectorAll(‘script[type=\’text\/js-worker\’]’)
  • window.URL.createObjectURL(new Blob([‘’]))
    Reference https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers