run in this way,   no why,   only for you heart
CSDN博客(点击进入) CSDN
51CTO(点击进入) 51CTO

JS 执行机制

JavaScript执行机制,重点有两点:
1.JavaScript是一门单线程语言。
2.Event Loop(事件循环)是JavaScript的执行机制。

既然说js是单线程,那就是在执行代码的时候是从上往下执行的,先来看一段代码:

   setTimeout(function(){
       console.log('定时器开始')
   });
   new Promise(function(resolve){
       console.log('Promise开始');
       resolve();
   }).then(function(){
       console.log('执行then函数')
   });
   console.log('代码执行结束');

在这里插入图片描述
关于javascript
    javascript是一门单线程语言,在最新的HTML5中提出了Web-Worker, 但javascript是单线程这一核心扔未改变。所以一切javascript版的“多线程”都是用单线程模拟出来的,一切javascript多线程都是纸老虎

js为什么是单线程的
    最初设计JS是用来在浏览器验证表单操控DOM元素的是一门脚本语言,如果js是多线程的,那么两个线程同时对一个DOM元素进行了相互冲突的操作,那么浏览器的解析器是无法执行的。

js为什么需要异步
    如果js中不存在异步,只能自上而下执行,如果上一行解析时间很长,那么下面的代码就会被阻塞。 对于用户而言,阻塞就以为着“卡死”,这样就导致了很差的用户体验。比如在进行ajax请求的时候如果没有返回数据后面的代码就没办法执行

js单线程又是如何实现异步的呢
    js中的异步以及多线程都可以理解成为一种“假象”,就拿h5的WebWorker来说,子线程有诸多限制,不能控制DOM,不能修改全局对象等等,通常只用来做计算做数据处理。
这些限制并没有违背我们之前的观点,所以说是“假象”。JS异步的执行机制其实就是事件循环(eventloop),理解了eventloop机制,就理解了js异步的执行机制。

js的事件循环(eventloop)是怎么运作的
    事件循环、eventloop\运行机制 这三个术语其实说的是同一个东西,“先执行同步操作异步操作排在事件队列里”这样的理解其实也没有任何问题但如果深入的话会引出很多其他概念,比如event table和event queue, 我们来看运行过程:

  1. 首先判断JS是同步还是异步,同步就进入主线程运行,异步就进入event table.
  2. 异步任务在event table中注册事件,当满足触发条件后,(触发条件可能是延时也可能是ajax回调),被推入event queue
  3. 同步任务进入主线程后一直执行,直到主线程空闲时,才会去event queue中查看是否有可执行的异步任务,如果有就推入主线程中。
    在这里插入图片描述
        那怎么知道主线程执行栈为空啊?js引擎存在monitoring process进程,会持续不断的检查 主线程 执行栈是否为空,一旦为空,就会去event queue那里检查是否有等待被调用的函数。
	let data = [];
	$.ajax({
	    url:www.javascript.com,
	    data:data,
	    success:() => {
	        console.log('发送成功!');
	    }
	})
	console.log('代码执行结束');
  • ajax进入event table,注册回调函数success
  • 执行 console.log(‘代码执行结束’)
  • ajax事件完成,回调函数success进入event queue
  • 主线程从event queue读取回调函数success并执行

setTimeout

	setTimeout(() => {
	  console.log('2秒到了')
	}, 2000)

    setTimeout是异步操作首先进入event table, 注册的事件就是它的回调,触发条件就是2秒之后,当满足条件回调被推入event queue,当主线程空闲时会去event queue里查看是否有可执行的任务。

	console.log(1) // 同步任务进入主线程
	setTimeout(fun(),0)   // 异步任务,被放入event table, 0秒之后被推入event queue里
	console.log(3) // 同步任务进入主线程

    1,3是同步任务马上会被执行,执行完成之后主线程空闲去event queue(事件队列)里查看是否有任务在等待执行,这就是为什么setTimeout的延迟事件是0毫秒却在最后执行的原因

    但setTimeout延时的时间有时候并不是那么准确

	setTimeout(() => {
	  console.log('2秒到了')
	}, 2000)
	wait(9999999999)

分析运行过程:

  1. console进入Event Table并注册,计时开始。
  2. 执行sleep函数,sleep方法虽然是同步任务但sleep方法进行了大量的逻辑运算,耗时超过了2秒
  3. 2秒到了,计时事件timeout完成,console进入Event queue, 但是sleep还没执行完,主线程还被占用,只能等着。
  4. sleep终于执行完了, console终于从event queue进入了主线程执行,这个时候已经远远超过了2秒

    其实延迟2秒只是表示2秒后,setTimeout里的函数被推入event queue , 而event queue(事件队列)里的任务,只有在主线程空闲时才会执行。
    上述流程走完,我们知道setTimeout这个函数,是经过指定时间后,把要执行的任务(本例子中为console)加入到event queue中, 又因为单线程任务要一个一个执行,如果前面的任务需要的时间太久,那么只能等着,导致真正的延迟时间远远大于2秒。 我们还经常遇到setTimeout(fn,0)这样的代码,它的含义是,指定某个任务在主线最早的空闲时间执行,意思就是不用再等多少秒了, 只要主线程执行栈内的同步任务全部执行完成,栈为空就马上执行。但是即便主线程为空,0毫秒实际上也是达不到的。根据HTML的标准,最低是4毫秒。

setIntval
    以setIntval(fn,ms)为例,setIntval是循环执行的,setIntval会每隔指定的时间将注册的函数置入event queue,不是每过ms会执行一次fn,而是每过ms秒,会有fn进入event queue。需要注意一点的是,一单setIntval的回调函数fn执行时间超过了延迟事件ms,那么就完成看不出来有时间间隔了。

文章转载自:https://www.jianshu.com/p/1368d375aa66,原文是对另外两篇文章的汇总,后面还有些内容关于宏任务和微任务,这部分内容可能牵扯到node.js,在不同平台上可能存在些差异,后面内容因此没有转载,下面就是相关文章的链接:
https://juejin.im/post/59e85eebf265da430d571f89
https://juejin.im/post/5c148ec8e51d4576e83fd836
https://juejin.im/post/5c36b3b0f265da611f07e409

posted @ 2019-11-24 16:02  _小龙人  阅读(129)  评论(0编辑  收藏  举报