由JS单线程机制引起的思考(包含循环机制与web worker)

由于JS设定之初,设计者并不希望过于复杂,所以将JS设置成单线程语言,不过由于后来语言发展,单线程反而成为历史遗留问题,不过这并不本文想讲解的内容。。。好了,开始正题。

我们都知道JS对面是从上至下,从左到右的顺序执行的。但是,这是在代码块内没有异步代码的情况下。一旦存在异步代码,就需要考虑JS的循环机制(又称浏览器循环机制)。理解js的事件循环机制,能够很大程度的帮我们更深层次的理解平时遇到的一些很疑惑的问题。

我们这里的JS 分为浏览器端的JS和服务器端的JS(如NodeJS),以下内容暂时只讲浏览器端JS的循环机制。

我们先来说一下概念,以下图说明:
事件循环机制草图
在浏览器的执行环境里:
⑴一个主线程,线程内包含堆栈;
⑵还有WebAPs,包含DOM操作、ajax请求以及setTimeout异步操作等;当在执行过程中遇到一些类似于setTimeout等异步操作的时候,会交给浏览器的其他模块(以webkit为例,是webcore模块)进行处理。对于图中WebAPIs提到的三种API,webcore分别提供了DOM Binding、network、timer模块来处理底层实现。
⑶队列其实是回调函数队列(网上不少人设定为任务队列)。不存放宏任务,微任务所有代码(包含关键字的代码),只存放其中包含的代码段(即回调函数)。但分宏任务的回调函数队列和微任务回调函数队列,以下简化为宏任务队列和微任务队列。

JS每句代码为一个任务,任务分为同步任务和异步任务。异步任务又分为宏任务(Macro Task)和微任务(Micro Task),微任务优先级高于宏任务。
同任务优先级高低从左至右排列如下:

  1. 宏任务: script(整体代码), setTimeout, setInterval, setImmediate(Nodejs,v8环境), I/O, UI rendering
  2. 微任务: process.nextTick(Nodejs,v8环境), Promises, Object.observe, MutationObserver
    async await是ES7内容,本质上是promise的语法糖,优先级与promise相同。
    PS:如果面试官问的是浏览器而不是NodeJS,注意说优先级时提一嘴运行环境。

然后再来说一下JS执行引擎如何处理JS。
首先,JS会从上至下,从左至右读取每行代码,每读取一次代码,都会将该代码块区分为同步任务还是异步任务,是异步任务的话会区分为宏任务还是微任务。若为同步任务,则放置于栈(执行栈)内执行,若为异步任务,则存放到WebAPIs中,等到时间达到指定时间时,会存入宏任务或微任务队列中。执行栈内函数执行完毕,然后会去微任务队列取任务,直到微任务队列清空。然后检查宏队列,去宏任务队列取任务,并且每一个宏任务执行完毕都会去微队列跑一遍,看看有没有新的微任务,有的话再把微任务清空。这样依次循环,直到全部任务执行完毕。

提升点:

  1. 如果在执行任务时加入了一个微任务,此时在处理宏任务,则会先处理完当前宏任务再去微任务队列处理新的微任务。
  2. 如果在执行任务时加入了一个宏任务,此时在处理微任务,会处理完微任务队列的所有微任务再去处理宏任务。如果在处理宏任务,则会在宏任务队列最后一个才处理这个昕加入的宏任务。

举个例子:

console.log(1);
    
 setTimeout(() => {
   console.log('setTimeout');
 }, 0);

 let promise = new Promise(resolve => {
   console.log(2);
   resolve();
 }).then(data => {
   console.log(3);
   let promises = new Promise(resolve => {
     console.log(4);
     resolve();
   }).then(data => {
      console.log(5);
   });
 });
  let promisess = new Promise(resolve => {
   console.log(6);
   resolve();
 }).then(data => {
   console.log(7);
   let promisesss = new Promise(resolve => {
     console.log(8);
     resolve();
   }).then(data => {
      console.log(9);
   });
 });   
 console.log(10);

其结果为:1,2,6,10,3,4,7,8,5,9,setTimeOut
解析:
首先执行同步代码,即执行出来1,2,6,10;然后执行promise里then的回调函数(因为promise为微任务,settimeout为宏任务,所以先执行promise里的微任务),同为微任务情况下,从上至下执行出3,4,7,8;现在执行环境里还剩嵌套在promise的then微任务和settimeout宏任务,所以继续执行嵌套在promise的then微任务,得出5,9;最后因为环境只剩下一个宏任务,所以执行最后宏任务得出setTimeOut。

你可能感兴趣的:(javascript)