标签:同步 必须 标签 wechat AC promise 分配 int page
既然今天要谈的是javascript的事件循环机制,要理解事件循环,首先要知道事件循环是什么。
我们先从一个例子来看一下javascript的执行顺序。
<script> setTimeout(function() { console.log(‘定时器开始了.‘); },0) new Promise(function(resolve) { console.log(‘马上执行for循环了‘); for (let i = 0; i < 10000; i++) { i == 99 && resolve(); } }).then(function() { console.log(‘执行then函数了‘); }) console.log(‘代码执行结束‘); //执行结果为: //马上执行for循环了 //代码执行结束 //执行then函数了 //定时器开始了. </script>
怎么样,是不是和自己在心里运行的结果差了一万八千里呢。如果是的话,请耐心看完后面的内容,让你彻底弄明白javascript的事件循环机制。
要想了解事件循环的我们就得从javascript的工作原理开始说起。
javascript语言的一大特点就是单线程,可是为什么javascript不做成多线程呢?
JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?
我们说单线程就意味着所有的任务必须排队。就类似于银行只有一个窗口,前一个任务执行结束后,后一个任务才能执行。如果新执行的任务耗时很长,那么后一个任务就不得不一直等着。
这样就又出现了一个问题,在进行浏览器的操作时,我们常常会通过ajax向后台发送请求,然而js必须等到浏览器接收到响应内容后才会继续往下执行,如果这段时间是10s,那么页面必须停在这里10s。这不仅会影响用户体验,也会降低CPU的利用率,显然不是我们想要的。
于是,聪明的程序员小哥哥就把任务分成了两类
同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;
异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行,通常来说拥有callback回调函数的任务就是异步任务。
同步任务和异步任务的执行过程大致可以简化成如下的导图所示。
<script> console.log(1); setTimeout(function task() {
console.log(‘定时器执行了.‘);
},1000);
console.log(2); </script>
我们不禁要问了,那怎么知道主线程执行栈为空啊?js引擎存在monitoring process进程,会持续不断的检查主线程执行栈是否为空,一旦为空,就会去Event Queue那里检查是否有等待被调用的函数。
其实除了广义的同步任务和异步任务的划分,异步任务还可以细分为宏任务(macrotask) 与微任务( microtask)。不同的异步任务类型会进入不同的Event Queue。
宏任务: 需要多次事件循环才能执行完,事件队列中的每一个事件都是一个宏任务。每次事件循环都会调入一个宏任务,浏览器为了能够使得js内部宏任务与DOM任务有序的执行,会在一个宏任务执行结束后,在下一个宏执行开始前,对页面进行重新渲染 (task->渲染->task->…)。例如鼠标点击会触发一个事件回调,需要执行一个宏任务,然后解析HTML。setTimeout的作用是等待给定的时间后为它的回调产生一个新的宏任务。
微任务: 微任务是一次性执行完的。微任务通常来说是需要在当前task执行结束后立即执行的任务,例如对一些动作做出反馈或者异步执行任务又不需要分配一个新的task,这样便可以提高一些性能。只要执行栈中没有其他的js代码正在执行了,而且当前调入的宏任务都执行完了,微任务队列会立即执行。如果在微任务执行期间微任务队列加入了新的微任务,会将新的微任务加入队列尾部,之后也会被执行。
简单理解,宏任务在下一轮事件循环执行,微任务在本轮事件循环的所有任务结束后执行。
据whatwg规范介绍:
宏任务、微任务执行流程图如下所示。
这时,我们再来看一下文章开头给的一段代码。
<script> setTimeout(function() { console.log(‘定时器开始了.‘); },0) new Promise(function(resolve) { console.log(‘马上执行for循环了‘); for (let i = 0; i < 10000; i++) { i == 99 && resolve(); } }).then(function() { console.log(‘执行then函数了‘); }) console.log(‘代码执行结束‘); //执行结果为: //马上执行for循环了 //代码执行结束 //执行then函数了 //定时器开始了. </script>
执行步骤如下所示。
javascript是一门单线程的语言,事件循环是js异步编程的一种方法。也是js的执行机制。当浏览器中的网页刚刚载入的时候,<script>里的代码会作为第一个宏任务被压入栈执行,同步代码执行完后,如果有微任务就执行微任务,没有微任务就执行下一个宏任务。如此往复循环,直至所有任务都执行完毕。
JavaScript 运行机制详解:再谈Event Loop
标签:同步 必须 标签 wechat AC promise 分配 int page
原文地址:https://www.cnblogs.com/yuliangbin/p/9192558.html