setTimeOut引发的思考——初步理解JS事件循环机制 Event Loop


JS是单线程引擎,在线程中拥有唯一一个事件循环(web workder涉及到了多线程,再做补充)

JS代码执行过程中,除了依靠函数调用栈顺序执行JS代码,还依靠任务队列(task queue)执行一些代码

一个线程中,事件循环是唯一的,但是任务队列可以有多个



任务队列

任务队列分为macro-task(宏任务)和micro-task(微任务),新标准中称为task和jobs。

macro-task包括script(代码整体)、setTimeout、setInterval、IO、UI rendering。

micro-task包括process.nextTick、Promise、MutationObserver(HTML5新特性)。

setTimeout/Promise为任务源,进入任务队列的是他们的执行任务(如回调函数)。


执行规则

  • 来自不同任务源的任务会进入到不同的任务队列。其中setTimeout与setInterval是同源的(?)。
  • 事件循环的顺序,决定了JavaScript代码的执行顺序。它从script(整体代码)开始第一次循环。之后全局上下文进入函数调用栈。直到调用栈清空(只剩全局),然后执行所有的micro-task。当所有可执行的micro-task执行完毕之后。循环再次从macro-task开始,找到其中一个任务队列执行完毕,然后再执行所有的micro-task,这样一直循环下去。
  • 其中每一个任务的执行,无论是macro-task还是micro-task,都是借助函数调用栈来完成。


异步执行的运行机制

  1. 所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
  2. 主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
  3. 一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
  4. 主线程不断重复上面的第三步。主线程不断重复上面的第三步。


    两个过程图解:
    事件循环机制0

事件循环机制1

有些博客中写的是script运行到比如setTimeout就放入事件队列中,这个说的有问题,是在你定义的timeout时间之后,将回调函数放入事件队列中,所谓macroTask/microTask是早期为了区分promise,process.netxTick等和其他如setTimeout执行顺序不一样而定义的,标准中并没有macroTask这么一说。


参考:

实例执行过程图解见:https://mp.weixin.qq.com/s/Of8gGz-EYuOkVSLqV8W2ow

JavaScript 运行机制详解:再谈Event Loop - 阮一峰的网络日志

猜你喜欢

转载自blog.csdn.net/Nana_9457/article/details/83008998