传送门: 彻底弄懂 JavaScript 执行机制、javascript执行机制
javascript是一门单线程的语言,本身不可异步。但是js的宿主(比如浏览器、node)却是多线程的。宿主环境通过某种方式(事件驱动,下文会讲)使得js具备了异步的属性。
Event Loop 是javascript的执行机制。
浏览器来看
js是单线程语言,浏览器只分配给js一个主线程,用来执行任务(函数),但一次只能执行一个任务,这些任务形成一个任务队列排队等候执行,但前端的某些任务是非常耗时的,比如网络请求,定时器和事件监听,如果让他们和别的任务一样,都老老实实的排队等待执行的话,执行效率会非常的低,甚至导致页面的假死。所以,浏览器为这些耗时任务开辟了另外的线程,主要包括http请求线程,浏览器定时触发器,浏览器事件触发线程,这些任务是异步的。下图说明了浏览器的主要线程
那么,这些其他线程如何回到主线程呢,没错,就是回调函数。比如定时器:
setTimeout(function(){
console.log(time is out);
},50);
执行这段代码的时候,浏览器异步执行计时操作,当50ms到了后,会触发定时事件,这个时候,就会把回调函数放到任务队列里。等待主线程任务完成执行。整个程序就是通过这样的一个个事件驱动起来的。
疑问是:那怎么直到主线程的执行栈为空啊?原因是:js引擎存在monitoring process进程,会持续不断的检查主线程执行栈是否为空,一旦为空,就会去event queue那里检查是否用等待被调用的函数。
接下来:
1.macro-task(宏任务):包括整体script代码,setInterval,setTimeout
2.micro-task(微任务):promise ,process.nexttrick(nodejs的内容)
不同类型的任务会进入对应的event queue,比如setInterval,setTimeout会进入相同的event queue。
事件循环的顺序决定js代码的执行顺序。进入整体代码(宏任务)后,开始第一次循环。接着执行所有的微任务。然后再从宏任务开始,找到其中一个任务队列执行完毕,再执行所有的微任务。
执行顺序是:主线程 > 微任务 > 宏任务
关于主线程与渲染线程:
界面渲染线程是单独开辟的线程,是不是DOM一变化,界面就立刻重新渲染?
如果DOM一变化,界面就立刻重新渲染,效率必然很低,所以浏览器的机制规定界面渲染线程和主线程是互斥的,主线程执行任务时,浏览器渲染线程处于挂起状态。主线程结束,界面就立刻重新渲染。
异步的好处和适合的场景
当我们的程序需要大量I/O操作和用户请求时,js这个具备单线程,异步,事件驱动多种气质的语言是多么应景!相比于多线程语言,它不必耗费过多的系统开销,同时也不必把精力用于处理多线程管理,相比于同步执行的语言,宿主环境的异步和事件驱动机制又让它实现了非阻塞I/O,所以你应该知道它适合什么样的场景了吧!
部分摘自 https://www.cnblogs.com/woodyblog/p/6061671.html