您的位置:首页 > Web前端 > JavaScript

javascript既然是单线程语言 , 为什么会分主线程和消息线程(event loop) ?

2018-01-18 14:44 423 查看
转自:点击打开链接https://www.zhihu.com/question/35905242

作者:杜文
链接:https://www.zhihu.com/question/35905242/answer/129806690
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

纵观那近万字的作文,只有这么一段是直接回答题主问题的,但是回答还是有问题的!如果再几年前,这么说还行,但是现在,不行! 首先在浏览器端,我们的js代码已经可能不全是在一个线程中了,这是由于h5引入了web workers,详请请移步 使用 Web Workers。它允许我们开一个工作线程处理一些耗时的任务,工作线程通过postmessage向js ui线程(这个概念我们下文在说,在此可以简单理解为javascript的主线程)发送消息来进行线程间通信。 而我们在js 主线程中要通过监听web worker的message消息来获得工作线程中的数据,这里的关键在工作线程和主线程之见的通信,实际上,浏览器是通过一个消息队列来实现,工作线程发出数据时,通过事件event,向js主线程发一个通知,然后将数据插入到消息队列中,这样, js在主线程就能收到通知,并从消息队列中去除数据(这是一个基本的过程,具体实现视浏览器而定),了解了这一点,你现在在去看看web workers的api,是不是好理解了许多。也许到这里,你已然决定去取消之前点的赞了,但是别着急,送佛送到西。这只是反驳了igetit所说的javascript只有一个线程。或许有人会说ajax也是系统单独开了一个线程去执行的网络请求,那么h5没有引入web worker之前是不是也是说javascript也有多个县城呢? 错! 在没有引入web worker之前,javascript确确实实是运行在一个单线程里面!那ajax 怎么说? 回忆一下调用ajax的过程,我们是需要把成功回调传递给xhr,典型的代码如下:
xhr = new XMLHttpRequest();
xhr.onreadystatechange=function(){} //传入我们的回调
xhr.open(...)
xhr.send(...)
浏览器虽然会在一个单独的线程去进行网络请求,但是我们是通过传递一个回调的方式去处理数据,浏览器在网络请求成功后,然后会切换回js线程来执行我们的回调,也就是说我们所有的js代码都是在js线程中运行的。所以javascript确实是在一个单线程中,而web worker不同,我们的js代码可以运行在js主线程之外,这也是为什么不能在web worker里面直接共享js主线程中定义的变量,不能操作ui (dom树)的原因,因为根本不在一个线程!其实说的这里,可以继续往深的说,比如为什么不能在工作线程中操作ui? 太深了就跑题了,如果有人感兴趣,可以私我。我们稍微再说一些,比如igetit同学文章中提到了操作系统的进程和线程,既然你们都点过赞,那就证明已经看到了,可能有人会说,进程的地址空间是隔离的,但同一个进程的线程之见是可以共享数据的,为什么web worker和js 虽然不在同一线程,但是如果在同一个进程中,也是应该能共享变量的,为甚么web worker和js 主线程之见药通过消息对列这种复杂的方式来传递数据?为什么不允许在web worker中操作UI? 如果有这些问题问的很好,但是我并不打算在此问题中回答,毕竟和题主问题不大,还是那句话,如果有兴趣可以私我。
下面再看一个igetit同学对浏览器为什么设计成单线程,和JavaScript可否设计成多线程?的回答:说最初是因为硬件配置低,所以没有考虑多线程,后来有机会但是,好在js支持异步,也就没有实现。
我敢保证igetit同学没有做过系统原生的ui开发!无论是windows c开发,活着java 界面开发,一条黄金原则就是不要在其它线程中操作ui,android中如果发现在非ui线程中操作ui会直接抛出异常!好,继续说,其实阮一峰这篇文章中对
为什么JavaScript是单线程这个问题的回答是对的,也是最主要的原因传送门JavaScript 运行机制详解:再谈Event Loop。 线程之间同步是有开销的,并且面临着同步问题,如果所有的线程都能操作ui,一旦cpu发生线程切换,都会面临数据不完整的风险,例如a线程ui界面改了一半,cpu发生线程切换,b线程又去改同一处。而界面本质上来说只是操作系统对数据在显示器上的一个映射,各个线程操作的都是数据,这么一来,你改我也改,你还没改完我有改,你改了一半我接着改,那还怎么玩,所以要支持多线程,必须提供同步工具(然线程之间不会彼此发生冲突)。而如果在js中支持多线程,不仅会增加js虚拟机的复杂度,也会增加编码的复杂度(程序猿不得不自己处理好同步问题),所以,这才是js 为什么到现在还是一个主线程的本质原因(先忽略web worker)。
现在我们回到题主的问题,zeromike 同学的回答其实已经说明了问题,但大家赞却点错了地方。 正如ajax回调一样,js vm会将我们所有的回调都会放在一个队列当中,比如我们监听的某个单击事件的回调,当用户单击了我们监听的元素,浏览器捕获到事件,然后就去执行我们的回调,而执行回调的环境都在同一个javasricpt线程中,其实也就是说event loop是在浏览器中的,而javascript是运行在同一个线程当中的!这也是js的特点-异步,node中也是延续了这个特点,当然,为了利用多核cpu,node 提供了child_process 。但这不是严格意义上的多线程,相当于起了多个node实例,也就有多个js线程。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  js单线程