JavaScript的运行机制Event Loop(事件循环)

来源:互联网 发布:阿里云cdn怎么收费 编辑:程序博客网 时间:2024/06/04 23:40

我们都知道JavaScript是一个单线程的语言。单线程也就意味着同一时间只能做一件事。
JavaScript之所以设计为单线程,是因为它的用途主要还是用来操作DOM,为了避免复杂性,所以JavaScript在诞生之初就是单线程的,这也是这么语言的核心特征,未来也不会改变。
虽然HTML5为了尽可能地利用CPU的计算能力而推出了Web Worker标准,允许JavaScript脚本创建多个线程,但是需要注意的是子线程必须受主线程的控制,并且还不能操作DOM。所以,这个标准并没有改变JavaScript的单线程本质。

任务队列

单线程就意味着所有任务都需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。

但是这种效率很低,所以JavaScript的任务分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入’任务队列’(task queue)的任务,只有’任务队列’通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

执行过程如下:

  1. 所有同步任务都在主线程执行,形成一个执行栈.
  2. 所有异步任务都在任务队列中(task queue) 。只要异步任务有了运行结果,就在‘任务队列’之中放置一个事件.(这个事件是一个flag)
  3. 等线程执行完执行栈中的同步任务之后,系统会读取‘任务队列’,看看里面有哪些事件,然后把等待执行的任务(有flag的任务)推入到执行栈中,开始执行。
  4. 主线程不断重复第三步。

    这里写图片描述

只要主线程空了,就会读取‘任务队列’。这就是js的运行机制。

事件和回调函数

任务队列其实是一个事件队列,因为里面的异步任务在有了运行结果(IO设备,用户产生的事件包括click等,ajax,定时器)之后,就会在‘任务队列’中添加一个事件,表示相关的异步任务可以进入‘执行栈’了。主线程读取异步队列,其实就是读取里面的事件。

异步任务必须指定回调函数,ES7的async,await 其实也是指定了回调函数的,只不过是用同步写法写异步。
‘任务队列’是一个先进先出的数据结构,排在前面的事件,优先被主线程读取。
再次强调的是,是排在前面的事件,而不是排在前面的异步任务,因为主线程读取的是事件。
看个例子:

console.log(1)setTimeout(function(){console.log(2)}, 5)setTimeout(function(){console.log(3)}, 4);console.log(4);

如果读取的是异步任务的话,那么应该是1,4,2,3
而结果是1,4,3,2
因为异步队列中的两个任务,第二个任务先有运行结果,先获得flag事件。所以排在task queue的前面,先被读取到。

另外需要注意的是,对于setTimeout这类定时器只是将事件插入到了‘任务队列’里,必须要等到执行栈中的代码执行完之后,主线程才会执行它指定的回调函数。所以说如果执行栈中的代码耗时很长,就有可能等很久,所以没办法保证回调函数一定会在setTimeout()指定的时间执行。

Event Loop

主线程中‘任务队列’中读取事件,这个过程是循环不断的。所以这种运行机制叫做Event Loop(时间循环)。

这里写图片描述

0 0