一直只是知道,js是单线程,栈放基本类型的变量和一些对象的引用,堆放复杂类型的变量(对象),但是,究竟js是怎么样利用单线程的机制实现同步,异步?怎么样出栈入栈的?一直糊里糊涂。今天好好补一下。
首先来说单线程:
众所周知,js的核心特点就是单线程,那为什么不选择效率高的多线程呢,这要从最初定义它的用途说起。js 是浏览器脚本语言,它主要用来用户交互和操作DOM,这决定了它只能是单线程。因为,如果是多线程,那比如同时多个线程在操作一个DOM元素,那究竟该取哪个?如果再加锁之类的操作,那就太复杂了,所以,js选择了单线程。
那就有人要问了,那单线程是不是就意味着js始终是一个任务执行完毕再执行下一个?那怎么做到异步呢?这就是接下来要说的js运行机制了。
Event Loop(事件循环):
先盗用一张图((引自Philip Roberts的演讲《Help, I'm stuck in an event-loop》))
请按照如下顺序理解:
1.js在运行时会有一个主线程(当前执行的任务,放在stack中)和一个任务队列(queue);
2.主线程里放着所有的同步任务,每个任务排队执行,只有上一个执行完毕,才能执行下一个任务;主线程的执行任务形成一个执行栈(后进先出),执行栈在执行的过程中(包括交互触发),调用webapi产生异步任务;
3.任务队列里放着所有的异步任务,当某个异步任务有了运行结果后,就会在任务队列中放置一个事件;
4.当主线程中的执行栈为空时,意味着所有同步任务已执行完成,此时,系统就会去任务队列中查看有哪些事件在等待,该事件对应的任务就会进入执行栈执行;
5.当该执行栈再执行完后,就又会去任务队列查看;
6.4与5不断循环;
以上就是event loop;
此时,估计会有疑问,那遇到耗时长的操作运算,就一直等着?
当然,我们是要想办法的,或者把它拆分执行,或者搞成异步等。比如:HTML5提出了Web Worker,它会在当前JavaScript的执行主线程中利用Worker类新开辟一个额外的线程来加载和运行特定的JavaScript文件,这个新的线程和JavaScript的主线程之间并不会互相影响和阻塞执行,而且在Web Worker中提供了这个新线程和JavaScript主线程之间数据交换的接口:postMessage和onMessage事件。但在HTML5 Web Worker中是不能操作DOM的,任何需要操作DOM的任务都需要委托给JavaScript主线程来执行,所以虽然引入HTML5 Web Worker,但仍然没有改线JavaScript单线程的本质。