事件循环笔记

JavaScript为什么单线程?

JS作为浏览器的脚本语言,需要处理用户交互和操作DOM,这决定它只能是一个单线程语言。因为假定JS是多线程的话,两个线程同时操作DOM会导致冲突。HTML5提出了Web Worker标准,允许JS创建多线程,但是也有很多限制,比如子线程完全受主线程控制,不能操作DOM。JS单线程的本质并没有改变。

任务队列

任务分为两种:

  • 同步任务
  • 异步任务

同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入”任务队列”(task queue)的任务,只有”任务队列”通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

具体来说,异步执行的运行机制如下。(同步执行也是如此,因为它可以被视为没有异步任务的异步执行。)

(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。

(2)主线程之外,还存在一个”任务队列”(task queue)。只要异步任务有了运行结果,就在”任务队列”之中放置一个事件。

(3)一旦”执行栈”中的所有同步任务执行完毕,系统就会读取”任务队列”,看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。

(4)主线程不断重复上面的第三步。

Event Loop

主线程从”任务队列”中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。

当javascript代码执行的时候会将不同的变量存于内存中的不同位置:堆(heap)和栈(stack)中来加以区分,heap中一般存储我们的变量,stack一般存储函数或者方法。stack叫做执行栈,我们的方法依次会在这里执行。执行栈事件先进后出,任务队列先进先出。web apis则是代表一些异步事件,而callback queue即事件队列。

对照图片我们解释一下整个事件循环机制

  • 执行栈执行主线程任务,当有操作domajax交互使用定时器异步操作的时候,这些任务会被移入到 callback queue 任务队列中
  • 当主线程任务执行完毕为空时,会读取callback queue队列中的函数,进入主线程执行
  • 上述过程会不断重复,也就是常说的Event Loop(事件循环)。

不同的异步任务被分为两类:微任务(micro task)和宏任务(macro task)。

在一个事件循环中,异步事件返回结果后会被放到一个任务队列中。然而,根据这个异步事件的类型,这个事件实际上会被对应的宏任务队列或者微任务队列中去,当执行栈为空的时候,主线程会首先查看微任务中的事件,如果微任务不是空的那么执行微任务中的事件,如果没有在宏任务中取出最前面的一个事件。把对应的回调加入当前执行栈…如此反复,进入循环。

  • macro-task(宏任务)
    • setTimeout
    • setInterval
    • setImmediate
  • micro-task(微任务)
    • Promise
    • process.nextTick

参考