关于javascript:JavaScript执行机制

3次阅读

共计 6061 个字符,预计需要花费 16 分钟才能阅读完成。

纲要
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 Queue
setTimeout1         process1
setTimeout2         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 Queue
setTimeout2           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…

正文完
 0