JS运行机制(同步、异步、Event Loop)
一、JS为什么是单线程的
Javascript语言的一大特点就是单线程,同一时间只能做同一件事,那么为什么JS不能多线程呢?
作为浏览器脚本语言,Javascript的主要用途是与用户互动,以及操作DOM,这决定了它只能是单线程,否则会带来很复杂的同步问题。比如:假定Javascript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了找个节点,这时浏览器应该以哪一个为准?
所以,为了避免复杂性,从一诞生,Javascript就是单线程,这已经成为了这门语言的核心特征,将来也不会改变。
为了利用多核CPU的计算能力,HTML5提出Web Woker标准,允许Javascript脚本创建多个线程,但是子线程完全受主线程控制,且不能操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。
二、同步和异步
假设存在一个函数A:
A(args…){…}
同步:如果在调用函数A的时候,调用者立即能够得到预期的结果,那么这个函数就是同步的。
异步:如果在调用函数A的时候,调用者无法立即得到预期的结果,而是需要在将来通过一定的手段(耗时,延时,事件触发)得到,那么这个函数就是异步的。
三、JS是如何实现异步操作的
虽然JS是单线程的但是浏览器的内核是多线程的,浏览器为一些耗时任务开辟了另外的线程,不同的异步操作会由不同的浏览器内核模块调度执行,例如onlcik,setTimeout,ajax处理的方式都不同,分别是由浏览器内核中的DOM Bingding、network、timer模块执行,当执行的任务得到运行结果时,会将对应的回调函数放到任务队列中。所以说,JS是一直都是单线程的,实现异步操作的其实是浏览器。
在上图中,调用栈中遇到DOM请求、ajax请求以及setTimeout等WebAPIs的时候就会交给浏览器内核的其他模块进行处理,webkit内核在Javascript执行引擎之外,有一个重要的模块是webcoew模块。对于图中WebAPIs提到的三种API,webcore分别提供了DOM Binding、network、timer模块来处理底层实现。等到这些模块处理完这些操作的时候将回调函数放入任务队列中,之后等栈中的task执行完之后再去执行任务队列中的回调函数。
小结:
1.所有的代码都要通过函数调用栈中调用执行
2.当遇到前文中提到的APIs的时候,会交给浏览器内核的其他模块进行处理
3.任务队列中存放的是回调函数
4.等到调用栈中的task执行完之后再回去执行任务队列中的task
JS的运行机制如下:
(1)所有的同步任务都在主线程上执行,形成一个执行栈。
(2)主线程之外,还存在一个“任务队列”。只要异步任务有了运算结果,就在“任务队列“之中放置一个事件(回调函数)。
(3)一旦“执行栈”中的所有同步任务执行完毕,系统就会读取“任务队列”,看看里面有哪些事件。那些对应的异步任务,于是结束等待状态。进入执行栈,开始执行。
(4)主线程不断重复上面的第三步
任务队列
上文中一直都有提到任务队列,那么任务队列到底是个什么东东呢?举个