JavaScript- Event Loop

一:什么是单线程
你必定据说过一句话:JavaScript是单线程的语言。单线程就是每个当下,只有一件事情能被处理。相反地,做为人类,咱们可以一边写代码,一边听歌,这是由于咱们的大脑不是单线程的。假如把【写代码】和【听歌】看作是咱们的JS代码,对应咱们人类大脑的就是JS引擎,那么在每个特定的时间点,JS引擎只能执行【写代码】或者【听歌】,不能2者同时进行,这就是JS的单线程。函数

二:单线程怎样保证不被阻塞 - Event Loop
想象一个场景:咱们经过发送一个HTTP请求去拿到用户的名字,而后再用这个拿到的名字去render咱们的页面。下面是一段伪代码:oop

const name = HTTP.get('/user/{id}').reponse.name;
renderPage(name);

由于JS是单线程的,那就意味着咱们在执行第一行代码(http请求)的时候,别的什么事情都不能作,如今整个页面都是卡住的,不能响应用户的任何操做。这势必是一个很是很差的体验。那么怎样保证咱们的事件处理不被阻塞呢?答案就是Event Loop(事件轮询)线程

什么是Event Loop呢?能够类比为你天天上班,早上你一到公司你的领导就给你安排了10件不一样的任务。虽然前面我说咱们人类是能够一边听歌,一边写代码的。可是通常工做上的事情,仍是得一件一件地作。因此,你把这10件任务写进你今天的【to do list】。每作完一个任务,你就把它划掉,而后立刻作下一个任务。code

三:JS Stack和Task Queue
咱们都知道代码执行,会有一个入栈和出栈的过程,在JS的这个语境下,咱们暂且把这个栈叫作JS Stack。咱们前面说了,JS是单线程的,这就意味着咱们只有一个JS Stack。因此将要被执行的代码,都得进入这个惟一的JS Stack,执行完了的代码,则从JS Stack移除掉。server

咱们的一段JS代码,包含着多个不一样的task,就如一个to do list,包含着多个不一样的任务同样。咱们的任务有轻重缓急之分,有着不一样的优先级。咱们的JS task也同样,有着不一样的类别,通常来讲JS Task能够分为如下2类:事件

1: MacroTask: script(总体代码), setTimeout, setInterval, setImmediate, requestAnimationFrame, I/O, UI Rendering
2: MicroTask: Promise, queueMicrotask, process.nextTick, Object.observe, MutationObserver

当咱们的JS引擎去遇到多个task的时候,就会把这些task进行排队,从而就造成了task queue。前面咱们说了task能够分为2类,天然地就会有2个类型的task queue:MacroTask queueMicroTask queueip

JS的同步代码会按照代码的出现顺序,放入JS Stack被执行。只有当前的JS Stack空了的时候,MacroTask queue或者MicroTask queue被推入JS Stack,继而被执行get

咱们立刻来看一下例子,加深对上面这段加粗的文字的理解:回调函数

setTimeout(function f1(){console.log('2')}, 0);  
console.log('1');

上面代码的输出结果是1,2,而不是2,1。要理解这个问题,咱们先来认识一下setTimeout()和Web API的概念。同步

四:ECMAScript和Web API
咱们常常在咱们的JS代码里面使用setTimeout(),可能天然地把setTimeout算做了ECMAScript的一部分。可是,其实并非这样的。咱们经常使用的setTimeout(),setInterval()等实际上是属于Web API的范畴。举个例子:

setTimeout(function f1(){}, 500);

以上代码的执行过程能够被理解为:Web API拿到setTimeout()的2个参数:回调函数f1()和延时500毫秒。Web API开始一个500毫秒的记时,在500毫秒到来以前,f1()会被Web API一直拿着,一旦500毫秒时间到,这个f1()就被推入MacroTask queue。

理解了Web API的工做过程以后,咱们再来回顾以前的例子:

setTimeout(function f1(){console.log('2')}, 0);  
console.log('1');

如今来分析一下上面这段代码的执行过程:
step1: setTimeout()方法进入JS Stack被执行,可是其实是调用Web API去执行。咱们的延时是0秒,就意味着Web API会当即把回调函数推入MacroTask queue。再次强调:进入task queue并不会被执行,要进入JS Stack才会被执行。

step2: 由于setTimeout()执行完了(注意是setTimeout()这个函数自己被执行了,而不是它的callback函数被执行了。),当前JS Stack为空,又由于console.log('1');是同步代码,因此会被当即放入JS Stack且执行,因而获得打印的值:1。

step3: 上一步执行完以后,当前JS Stack为空,因此从MacroTask queue里面取出f1()函数放入JS Stack执行,获得打印值: 2。

以上就是打印值1,2的由来。如今你明白了,为何明明setTimeout()在前面,可是打印值却在后面。

相关文章
相关标签/搜索