JS -- (19) 异步/同步 && 事件循环

101 阅读5分钟

一. 单线程与非阻塞

JavaScript从诞生之日起就是一门单线程的非阻塞的脚本语言。这是由其最初的用途来决定的:与浏览器交互。

单线程意味着,javascript代码在执行的任何时候,都只有一个主线程来处理所有的任务。 这是必要的,因为如果 JS 是多线程的,那么当两个线程同时对同一个 DOM 节点进行操作,一个添加,一个删除,那么就会出错,为了避免出现此类错误,就只能选择单线程模式!!!

单线程模式,大大降低了 JS 的执行效率。虽然,Web Worker 出现了,但是该技术实现的多线程有着诸多限制:

  • 所有新线程都受主线程的完全控制,不能独立执行。这意味着这些“线程” 实际上应属于主线程的子线程。
  • 这些子线程并没有执行I/O操作的权限,只能为主线程分担一些诸如计算等任务。

非阻塞 指的是当代码需要进行一项异步任务(无法立刻返回结果,需要花一定时间才能返回的任务,如I/O事件)的时候,主线程会挂起(pending)这个任务,然后在异步任务返回结果的时候再根据一定规则去执行相应的回调。

二. 异步与同步

<1> 同步

同步 指的是,如果一个函数,在其执行完毕返回时,调用者就可以拿到预期的返回值or看到预期的效果,那么这个函数就是同步的。

function A(){
   console.log(2);
}

Math.sqrt(2);

如上,第一个函数,在函数执行完毕返回时,就可以看到打印 2 ;第二个函数,执行完毕返回时,就可以得到执行结果 2的平凡根 。这样的函数都是同步的!!

<2> 异步

异步 ,与同步相对应,如果一个函数返回的时候,调用者还不能够得到预期结果,而是需要在将来通过一定的手段得到,那么这个函数就是异步的。

fs.readFile('foo.txt', 'utf8', function(err, data) {
   console.log(data);
});

如上,我们希望通过fs.readFile函数读取文件foo.txt中的内容,并打印出来。但是在fs.readFile函数执行完毕返回时,我们期望的结果并不会发生,而是要等到文件全部读取完成之后,才会调用传入的回调函数参数,进行打印。这样的函数fs.readFile就是异步的!!!【如果文件很大的话可能要很长时间】。

(1) 异步的执行过程
  • JS 引擎遇到一个异步事件,主线程会把该注册函数(事件挂起) ,并通知工作线程来执行
  • 相应的工作线程接收请求并告知主线程已收到(异步函数返回);
  • 主线程可以继续执行后面的代码,
  • 同时工作线程执行异步任务;
  • 工作线程完成工作后,JS 会把该事件放进其对应的事件队列中;
  • 事件队列中的事件并不会被立即执行, 而是当主线程空闲时,会依照事件队列的优先级,从某一队列中选取排在第一位的事件(回调函数),并把其放入执行栈中执行!!

其中包括两个要素:发起(注册)函数回调函数

setTimeout(fn, 1000);

其中的setTimeout就是异步过程的发起函数,fn是回调函数。

三. 事件循环与事件队列

<1> 事件循环

2162.png

如上图,JS 引擎的执行机制就是:【同步任务-->微任务-->宏任务】

  • 1 先执行所有同步任务,碰到异步任务放到任务队列中
  • 2 同步任务执行完毕,开始执行当前所有的异步任务
  • 3 先执行任务队列里面所有的微任务
  • 4 然后执行一个宏任务
  • 5 然后再执行所有的微任务
  • 6 再执行一个宏任务,再执行所有的微任务·······依次类推到执行结束。

而 3 ~ 6 这个过程,就是 事件循环(Event Loop) 【主线程空闲时,会不断从事件队列中取事件执行!!】从某个事件队列中取一个事件并执行的过程叫做一次循环

2163.png

<2> 微任务与宏任务

2164.png

微任务(JS引擎发起的)总是先于其他任务执行!!!!

<3> 事件队列

需要不同队列!!!

每个任务都有一个任务类型 , 同一个类型的任务必须在一个队列也就是一共有多个队列 , 不同类型的任务可以分属不同的队列,在一个次事件循环中,浏览器可以根据实际情况从不同的队列中取出任务执行

微队列一直存在,且微队列中的任务永远先于其他任务执行!!!

浏览器必须准备好一个微队列 , 微队列中的任务优先所有其他任务执行他里面的东西 所有都要给我等 连绘制任务 都要等 就是最高优先级了

常见事件队列!!!

  • 微队列 : 用户存放需要最快执行的任务 优先级最高
  • 交互列队 : 用于存放用户操作后产生的事件处理任务 , 优先级高
  • 延时队列 : 用于存放计时器到达后的回调任务 , 优先级中【setTimeOut()...】

详见1

详见2

详见3