JavaScript 引擎包含了栈和队列,栈中执行到某个函数,调用了 webapi, webapi 执行完把消息函数放入任务队列,再通过某种调度,消息函数被压入执行栈中运行,而调度中起核心作用的就是事件循环。
再讲解核心概念之前首先要给大家讲一个小故事。
包工头给小伙已经布置了一个任务,把一堆砖从地面搬到楼上,于是小伙就不紧不慢地搬着。这时,包工头接到了一个电话,说一会还会到几批水泥,也要搬上楼。包工头心想,现在已经雇了一个人了,不能再多雇了,多雇人就会增加成本,于是他就想让这个小伙一个人(就是所谓的单线程)把所有的活全干了。他想了一个好办法,只要有水泥到了并且看到小伙空着手下楼,就让他先把水泥搬走,然后接着搬砖。就这样,小伙一刻没歇着,最终把砖和水泥都搬上了楼,包工头在一旁开心地数钱了。怎么样,包工头是不是太招人恨了。
其实,今天端端君要讨论的这个话题事件循环就很像这个黑心的包工头了。区别就是它俩压榨的对象不一样。事件循环压榨 JS 引擎,只要有任务并且看它空闲就让它去执行,所以才让单线程的 JS 愣是有并发的效果。
这里有几个核心概念需要和大家解释下。
栈
一种后进先出的数据结构。函数被压入栈中,再被弹出执行,由于后进先出,所以后入者先执行。
队列
一种先进先出的数据结构。函数进入队列中,从一端进,另一端出,所以先入者先被执行。
WebAPI
环境提供的 API。像浏览器和NodeJS 中提供的 setTimeout等等。
事件循环在其中承担一个任务调度的角色。它会检查执行栈是否为空,如果为空,并且任务队列中有函数,就把函数压入执行栈中去执行。
所以,这几个东西之间的关系就像下面这张图描述的那样。
栈中执行到一个函数,会发起 ajax 请求并传递回调函数,ajax 执行完毕会把回调函数放入消息队列中,事件循环判断栈中为空并且消息队列有函数,就把回调函数压入栈中。
怎么样,是不是对事件循环熟悉多了?