纲要
  1、场景剖析
  2、执行机制相干知识点
  3、以实例来阐明JavaScript的执行机制
  4、相干概念

场景剖析

    /*以下这段代码的执行后果是什么?如果按照:js是依照语句呈现的程序执行这个理念,那么代码执行的后果应该是:    //"定时器开始啦"    //"马上执行for循环啦"    //"执行then函数啦"    //"代码执行完结"但后果并不是这样的,失去的后果是:    //"马上执行for循环啦"    //"代码执行完结"    //"执行then函数啦"    //"定时器开始啦"  */  setTimeout(function(){      console.log('定时器开始啦')  });  new Promise(function(resolve){      console.log('马上执行for循环啦');      for(var i = 0; i < 10000; i++){          i == 99 && resolve();      }  }).then(function(){      console.log('执行then函数啦')  });  console.log('代码执行完结');

2、执行机制相干知识点
2.1、对于javascript
  javascript是一门单线程语言,在最新的HTML5中提出了Web-Worker,但javascript是单线程这一外围仍未扭转。所以所有javascript版的"多线程"都是用单线程模仿进去的。

2.2、javascript的同步和异步
  单线程就意味着,所有工作须要排队,前一个工作完结,才会执行后一个工作。如果前一个工作耗时很长,后一个工作就不得不始终等着。
  如果排队是因为计算量大,CPU忙不过来,倒也算了,然而很多时候CPU是闲着的,因为IO设施(输入输出设施)很慢(比方Ajax操作从网络读取数据),不得不等着后果进去,再往下执行。
  JavaScript语言的设计者意识到,这时主线程齐全能够不论IO设施,挂起处于期待中的工作,先运行排在前面的工作。等到IO设施返回了后果,再回过头,把挂起的工作继续执行上来。
  于是,所有工作能够分成两种,一种是同步工作(synchronous),另一种是异步工作(asynchronous)。同步工作指的是,在主线程上排队执行的工作,只有前一个工作执行结束,能力执行后一个工作;异步工作指的是,不进入主线程、而进入"工作队列"(task queue)的工作,只有"工作队列"告诉主线程,某个异步工作能够执行了,该工作才会进入主线程执行。

  1、同步和异步工作别离进入不同的执行"场合",同步的进入主线程,异步的进入Event Table并注册函数。
  2、当Event Table中指定的事件实现时,会将这个函数移入Event Queue。
  3、主线程内的工作执行结束为空,会去Event Queue读取对应的函数,进入主线程执行。
  4、上述过程会一直反复,也就是常说的Event Loop(事件循环)。
  5、咱们不禁要问了,那怎么晓得主线程执行栈为空啊?js引擎存在monitoring process过程,会继续一直的查看主线程执行栈是否为空,一旦为空,就会去Event Queue那里查看是否有期待被调用的函数。

2.3、JavaScript的宏工作与微工作
  你是否感觉同步异步的执行机制流程就是JavaScript执行机制的全副?不是的,JavaScript除了狭义上的的同步工作何异步工作,其对工作还有更精密的定义:
    macro-task(宏工作):包含整体代码script,setTimeout,setInterval
    micro-task(微工作):Promise,process.nextTick
  不同类型的工作会进入对应的Event Queue。
  事件循环的程序,决定js代码的执行程序。进入整体代码(宏工作)后,开始第一次循环。接着执行所有的微工作。而后再次从宏工作开始,找到其中一个工作队列执行结束,再执行所有的微工作。

3、以实例来阐明JavaScript的执行机制

 3.1、同步console.log(1);console.log(2);console.log(3);/*    执行后果:1、2、3    同步工作,依照程序一步一步执行*/

3.2、同步和异步

console.log(1);setTimeout(function() {    console.log(2);},1000)console.log(3);/*    执行后果:1、3、2    同步工作,依照程序一步一步执行    异步工作,放入音讯队列中,期待同步工作执行完结,读取音讯队列执行*/

3.3、异步工作进一步剖析

console.log(1);setTimeout(function() {    console.log(2);},1000)setTimeout(function() {    console.log(3);},0)console.log(4);/*    猜想是:1、4、2、3   但实际上是:1、4、3、2    剖析:        同步工作,依照程序一步一步执行        异步工作,当读取到异步工作的时候,将异步工作搁置到Event table(事件表格)中,当满足某种条件或者说指定事件实现了(这里的是工夫别离是达到了0ms和1000ms)当指定事件实现了才从Event table中注册到Event Queue(事件队列),当同步事件实现了,便从Event Queue中读取事件执行。(因为3的事件先实现了,所以先从Event table中注册到Event Queue中,所以先执行的是3而不是在后面的2)*/

3.4、宏工作和微工作

console.log(1);setTimeout(function() {    console.log(2)},1000);new Promise(function(resolve) {    console.log(3);    resolve();}).then(function() {    console.log(4)});console.log(5);/*    以同步异步的形式来判断的后果应该是:1、3、5、2、4    然而事实上后果是:1、3、5、4、2    为什么是这样呢?因为以同步异步的形式来解释执行机制是不精确的,更加精确的形式是宏工作和微工作:    因而执行机制便为:执行宏工作 ===> 执行微工作 ===> 执行另一个宏工作 ===> 一直循环        即:在一个事件循环中,执行第一个宏工作,宏工作执行完结,执行以后事件循环中的微工作,执行结束之后进入下一个事件循环中,或者说执行下一个宏工作*/

3.5、是否彻底了解JavaScript执行机制实例

console.log('1');setTimeout(function() {    console.log('2');    process.nextTick(function() {        console.log('3');    })    new Promise(function(resolve) {        console.log('4');        resolve();    }).then(function() {        console.log('5')    })})process.nextTick(function() {    console.log('6');})new Promise(function(resolve) {    console.log('7');    resolve();}).then(function() {    console.log('8')})setTimeout(function() {    console.log('9');    process.nextTick(function() {        console.log('10');    })    new Promise(function(resolve) {        console.log('11');        resolve();    }).then(function() {        console.log('12')    })})/*

1、 第一轮事件循环流程剖析如下:

整体script作为第一个宏工作进入主线程,遇到console.log,输入1。遇到setTimeout,其回调函数被散发到宏工作Event Queue中。咱们暂且记为setTimeout1。遇到process.nextTick(),其回调函数被散发到微工作Event Queue中。咱们记为process1。遇到Promise,new Promise间接执行,输入7。then被散发到微工作Event Queue中。咱们记为then1。又遇到了setTimeout,其回调函数被散发到宏工作Event Queue中,咱们记为setTimeout2。     宏工作Event Queue   微工作Event QueuesetTimeout1         process1setTimeout2         then1 上表是第一轮事件循环宏工作完结时各Event Queue的状况,此时曾经输入了1和7。咱们发现了process1和then1两个微工作。执行process1,输入6。执行then1,输入8。 好了,第一轮事件循环正式完结,这一轮的后果是输入1,7,6,8。 

2、 那么第二轮工夫循环从setTimeout1宏工作开始:

 首先输入2。接下来遇到了process.nextTick(),同样将其散发到微工作Event Queue中,

记为process2。new Promise立刻执行输入4,then也散发到微工作Event Queue中,记为then2。

 宏工作Event Queue     微工作Event QueuesetTimeout2           process2                      then2                       第二轮事件循环宏工作完结,咱们发现有process2和then2两个微工作能够执行。    输入3。    输入5。    第二轮事件循环完结,第二轮输入2,4,3,5。

3、 第三轮事件循环开始,此时只剩setTimeout2了,执行。

    间接输入9。    将process.nextTick()散发到微工作Event Queue中。记为process3。    间接执行new Promise,输入11。    将then散发到微工作Event Queue中,记为then3。     宏工作Event Queue     微工作Event Queue                        process3                        then3     第三轮事件循环宏工作执行完结,执行两个微工作process3和then3。    输入10。    输入12。    第三轮事件循环完结,第三轮输入9,11,10,12。整段代码,共进行了三次事件循环,残缺的输入为1,7,6,8,2,4,3,5,9,11,10,12。

*/
4、相干概念
4.1、JS为什么是单线程的?
  JavaScript语言的一大特点就是单线程,也就是说,同一个工夫只能做一件事。那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊。
  JavaScript的单线程,与它的用处无关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很简单的同步问题。比方,假设JavaScript同时有两个线程,一个线程在某个DOM节点上增加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?
  所以,为了防止复杂性,从一诞生,JavaScript就是单线程,这曾经成了这门语言的外围特色,未来也不会扭转。
  为了利用多核CPU的计算能力,HTML5提出Web Worker规范,容许JavaScript脚本创立多个线程,然而子线程齐全受主线程管制,且不得操作DOM。所以,这个新规范并没有扭转JavaScript单线程的实质。

4.2、JS为什么须要异步?
  如果JS中不存在异步,只能自上而下执行,如果上一行解析工夫很长,那么上面的代码就会被阻塞。 对于用户而言,阻塞就意味着"卡死",这样就导致了很差的用户体验。

4.3、JS单线程又是如何实现异步的呢?
  既然JS是单线程的,只能在一条线程上执行,又是如何实现的异步呢?
  是通过的事件循环(event loop),了解了event loop机制,就了解了JS的执行机制。

4.4、工作队列
  "工作队列"是一个事件的队列(也能够了解成音讯的队列),IO设施实现一项工作,就在"工作队列"中增加一个事件,示意相干的异步工作能够进入"执行栈"了。主线程读取"工作队列",就是读取外面有哪些事件。
  "工作队列"中的事件,除了IO设施的事件以外,还包含一些用户产生的事件(比方鼠标点击、页面滚动等等)。只有指定过回调函数,这些事件产生时就会进入"工作队列",期待主线程读取。
  所谓"回调函数"(callback),就是那些会被主线程挂起来的代码。异步工作必须指定回调函数,当主线程开始执行异步工作,就是执行对应的回调函数。
  "工作队列"是一个先进先出的数据结构,排在后面的事件,优先被主线程读取。主线程的读取过程基本上是主动的,只有执行栈一清空,"工作队列"上第一位的事件就主动进入主线程。然而,因为存在后文提到的"定时器"性能,主线程首先要检查一下执行工夫,某些事件只有到了规定的工夫,能力返回主线程。
  读取到一个异步工作,首先是将异步工作放进事件表格(Event table)中,当放进事件表格中的异步工作实现某种事件或者说达成某些条件(如setTimeout事件到了,鼠标点击了,数据文件获取到了)之后,才将这些异步工作推入事件队列(Event Queue)中,这时候的异步工作才是执行栈中闲暇的时候能力读取到的异步工作。

4.5、Event Loop
  主线程从"工作队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。
  Event Loop是javascript的执行机制

4.6、setTimeout(fn,0)
  setTimeout(fn,0)的含意是,指定某个工作在主线程最早可得的闲暇工夫执行,也就是说,尽可能早得执行。它在"工作队列"的尾部增加一个事件,因而要等到同步工作和"工作队列"现有的事件都解决完,才会失去执行。
  HTML5标准规定了setTimeout()的第二个参数的最小值(最短距离),不得低于4毫秒,如果低于这个值,就会主动减少。在此之前,老版本的浏览器都将最短距离设为10毫秒。另外,对于那些DOM的变动(尤其是波及页面从新渲染的局部),通常不会立刻执行,而是每16毫秒执行一次。这时应用requestAnimationFrame()的成果要好于setTimeout()。
  须要留神的是,setTimeout()只是将事件插入了"工作队列",必须等到以后代码(执行栈)执行完,主线程才会去执行它指定的回调函数。要是以后代码耗时很长,有可能要等很久,所以并没有方法保障,回调函数肯定会在setTimeout()指定的工夫执行。

参考网址
https://juejin.im/post/59e85e...
https://www.cnblogs.com/Maste...
https://blog.csdn.net/highboy...