初识EventLoop

EventLoop,也就是平时所说的事件循环,那什么是eventloop呢?这里大致介绍一下。

首先,JavaScript是一个单线程的语言,为了解决多任务的问题,采用了一种叫做异步的方式,在看异步之前,我们先来看看什么是同步。

  • 同步操作

    设想一个场景:

    同步操作

    在这个场景中,JavaScript首先进行了一个I/O操作,但由于I/O操作很耗时间,JavaScript又是单线程的,所以JavaScript就要等待I/O操作返回结果,而这时JavaScript引擎就会处于空闲时间,等到I/O操作返回结果之后,引擎再进行处理。

    之后我们发送一个HTTP请求,请求之外再次等待服务器返回结果,然后才能处理。

    把在这个过程画出来就是如上图所示,图中阴影部分为等待时间,可以看出,等待时间远远长于引擎运行时间。这就是同步操作,即按顺序运行每一个任务。

  • 异步操作

    还是上面按个场景:

    异步操作

    这次我们换一种方式,让JavaScript发送完请求之后,不等待结果返回而直接执行之后的代码和任务,然后等到结果返回之后,调用事先设置好的回调函数来处理返回结果,这就是异步操作。

    可能有人会觉得奇怪,不是说JavaScript是一个单线程语言吗,它哪里来的另一个线程去在执行同步任务的同时去执行那些耗时操作呢,其实虽然浏览器提供的JavaScript引擎是单线程的,但是浏览器还提供了引擎之外的一些其他东西,比如Web API,这些都是可以调用的其他线程。举个例子来说,JavaScript发送一条HTTP请求,之后继续执行主线程的同步任务,而等待返回结果这件事就交给了浏览器,等到结果返回之后,浏览器通知JavaScript引擎来调用回调函数(其实是返回结果之后将回调函数放入了任务队列,下文细讲)。

    从上面的描述可以知道,JavaScript通过异步,可以大大提高引擎利用效率。

说完了什么是同步什么是异步,我们来看看eventloop(这里只说浏览器环境下的eventloop,不谈node环境下的)。在JavaScript中,存在一个执行栈,这个执行栈可以看做是JavaScript的主线程,当我们调用一个函数时,JavaScript引擎会将函数push到执行栈(Stack)中,执行完毕之后pop出去,当遇到异步任务时(例如setTimeout),就将任务放进任务队列中。异步任务有两种,一种叫做macrotask(简称task,宏任务,在ES6规范中称为Job),另一种是microtask(微任务)。

在遇到异步任务时,将任务添加进任务队列(先进先出)中,其中宏任务添加到宏任务队列中,微任务添加到微任务队列中,然后主线程继续往下执行同步任务,当主线程任务执行完毕之后,也就是Stack执行栈为空时,JavaScript引擎会首先看看微任务队列是否为空,不为空就取出微任务队列中所有微任务然后顺序执行,之后再从宏任务队列中取出第一个任务置入Stack中执行,执行时若有微任务继续放到微任务队列,当此事件执行完毕,还会把微任务队列中的微任务全部执行完毕,然后再去取宏任务队列中的异步任务。。。。如此反复循环,形成了eventloop事件循环。

下面用一张图展示一下:


eventloop

来看个例子:

console.log(0)
let p = Promise.resolve()
setTimeout(()=>{
    console.log(4);
    setTimeout(()=>{
        console.log(5);
    },0);
},0);
p.then(data=>{
    console.log(2);
    setTimeout(()=>{
        console.log(3);
    },0);
})
console.log(6)

输出是什么呢?

0 6 2 4 3 5

下面列举常见的macrotask和microtask:

  • macrotask

    setInterval, setTimeout, setImmediate(只有IE中能用), MessageChannel

  • microtask

    Promise的then, (MutationObserver)

你可能感兴趣的:(初识EventLoop)