十分钟大话Event Loop

294 阅读5分钟

JS运行机制

单线程

JavaScript是一门单线程的语言,这就意味着一个时间节点,Js只能处理一个任务。这是由于JS作为客户端脚本语言,它主要作用就是与用户进行交互,操作Dom节点。

试想如果多线程情况下,两个线程对同一个dom同时进行增上改查的操作,这时候的浏览器就不知道以哪一个线程为准了。

因此,为了语言的简单,JS被设计成了一门单线程的语言。即:JS在运行过程中,同一时间只能处理一个任务。

由于JavaScript是单线程的语言,它总是在一件任务完成以后才会进行下一件任务,因此如果当某一个任务十分耗的时候,例如进行一次网络请求,如果服务器不能马上响应,那么后面的任务都会处于阻塞的状态。这样的事情是绝对不允许发生的,因此JavaScript中通过同步任务、异步任务、任务队列和事件循环机制(Event Loop)去处理这个问题。

同步任务

同步任务是指直接在主线程中执行的任务。同步任务在主线程中按照代码的顺序,排队依此被主线程执行。

异步任务

异步任务是指当主线程执行到异步任务的代码时,它并不会立即执行异步任务,而是将异步任务放入回调队列(CallBack Queue)中,当回调队列中的事件被触发了,这时候异步任务会被放入任务队列中,当主线程中的同步任务都被执行完毕后,此时会从任务队列中按照先进先出的顺序,将任务队列中的异步任务依此取出放入主线程执行异步任务的代码。

任务队列

任务队列中依此放置所有的被触发的异步任务,主线程会在清空所有任务后,不段检查任务队列是不是为空,如果不为空会按照先进先出的规则,不断取出异步任务放入主进程的任务栈中执行。

案例理解

<script>
        console.log("Start");  //同步任务
        setTimeout(function(){
            console.log("Task1");//异步任务
        },1000);
        setTimeout(function(){
            console.log("Task2"); //异步任务
        },0);
        console.log('End');  //同步任务
</script>

这一段代依此输出的结果是:

Start 
End 
Task2 
Task1

别废话,show me animation!

这个动画很好的展示了控制台输出的结果的过程。我们来一步步看代码是如何执行的:

1、代码执行到console.log("Start");由于是同步任务,直接执行,在控制台输出Start

2、代码执行到Task1的setTimeout(),由于是异步任务,放入回调列队

3、代码执行到Task2的setTimeout(),由于是异步任务,放入回调列队

4、代码执行到console.log('End');由于是同步任务,直接执行,在控制台输出End

5、主线程的任务栈已经没有任务,主线程的任务栈试图不断从任务队列中读取任务,由于回调队列中的Task2的setTimeout()第二个延迟执行参数设置的是0毫秒(根据HTML5规范,设置4毫秒以下,默认设置为4毫秒),所以4毫秒后被放入了任务队列。主线程发现任务队列有任务,则把异步任务放入主线程的任务栈,进行执行,因此此时输出Task2

6、当Task1的setTimeout()设置的1000毫秒到后,Task1进入任务队列,这时候主线程的任务栈又处于空的状态,发现任务队列有任务,则将任务队列中的Task1放入任务栈中,进行执行,因此此时输出Task1

上面的整个流程就是事件循环机制的流程,总结一句话就是:主进程的任务栈在执行完所有同步任务后,会不断从任务队列中取出已经被触发的异步任务,进行执行。

哪些语句会被放入异步队列

1、setTimeOut和setInterval

2、DOM事件,例如:onClick、onChange等。

3、ES6中的Promise

什么时候被放入任务队列

在上面的动画中我们发现异步任务并不是马上被放入任务队列中的,而是事件被触发时,才会放入任务队列中。

例如:

setTimeout()当延迟的时间到达后,才会将回调函数放入任务队列,等待主线程的任务栈去取出异步的任务;

DOM事件中例如click事件,代码执行到addEventListener()它会把这个异步任务放入回调队列,当用户点击DOM,触发事件时,才会把异步任务放入任务队列中;

复盘

Javascript是一个单线程语言,但它通过利用异步任务和事件循环机制来避免执行耗时的任务,例如:网络任务等,阻塞线程。

在时间循环过程中,我们应该关注到,异步时间并不是马上被放入任务队列中,而是当异步任务被触发后,才会将异步任务放入任务队列中,等待主线程的任务栈来按照先进先出的规则,取出并执行任务。

我是大麦,如果喜欢我的文章,请给我一颗小心心。