javascript的执行机制—Event Loop

既然今天要谈的是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的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?

任务队列

我们说单线程就意味着所有的任务必须排队。就类似于银行只有一个窗口,前一个任务执行结束后,后一个任务才能执行。如果新执行的任务耗时很长,那么后一个任务就不得不一直等着。

这样就又出现了一个问题,在进行浏览器的操作时,我们常常会通过ajax向后台发送请求,然而js必须等到浏览器接收到响应内容后才会继续往下执行,如果这段时间是10s,那么页面必须停在这里10s。这不仅会影响用户体验,也会降低CPU的利用率,显然不是我们想要的。

于是,聪明的程序员小哥哥就把任务分成了两类

  • 同步任务
  • 异步任务

同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;

异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行,通常来说拥有callback回调函数的任务就是异步任务。

同步任务和异步任务的执行过程大致可以简化成如下的导图所示。

  • 同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入Event Table并注册函数。
  • 当指定的事情完成时,Event Table会将这个函数移入Event Queue。
  • 主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。
  • 上述过程会不断重复,也就是常说的Event Loop(事件循环)。

为了便于理解事件循环,我们来看一段代码。

<script>
    console.log(1);
    setTimeout(function task() {     console.log(‘定时器执行了.‘);   },1000);   console.log(2); </script>
  • js代码从上往下依次执行,
  • 遇到console.log(1),执行并打印出来。.
  • 遇到异步任务setTimeout,task进入Event Table并注册,计时开始。
  • 遇到console.log(2),执行并打印出来。
  • 主线程执行完毕,开始查询任务队列有没有等待执行的回调函数。
  • 一秒钟到后,timeout计时事件完成,task进入Event Queue。
  • 主线程发现任务队列有等待执行的函数task,将task调进进入主线程执行。

我们不禁要问了,那怎么知道主线程执行栈为空啊?js引擎存在monitoring process进程,会持续不断的检查主线程执行栈是否为空,一旦为空,就会去Event Queue那里检查是否有等待被调用的函数。

macrotask 与 microtask

其实除了广义的同步任务和异步任务的划分,异步任务还可以细分为宏任务(macrotask) 与微任务( microtask)。不同的异步任务类型会进入不同的Event Queue。

宏任务: 需要多次事件循环才能执行完,事件队列中的每一个事件都是一个宏任务。每次事件循环都会调入一个宏任务,浏览器为了能够使得js内部宏任务与DOM任务有序的执行,会在一个宏任务执行结束后,在下一个宏执行开始前,对页面进行重新渲染 (task->渲染->task->…)。例如鼠标点击会触发一个事件回调,需要执行一个宏任务,然后解析HTML。setTimeout的作用是等待给定的时间后为它的回调产生一个新的宏任务。

微任务: 微任务是一次性执行完的。微任务通常来说是需要在当前task执行结束后立即执行的任务,例如对一些动作做出反馈或者异步执行任务又不需要分配一个新的task,这样便可以提高一些性能。只要执行栈中没有其他的js代码正在执行了,而且当前调入的宏任务都执行完了,微任务队列会立即执行。如果在微任务执行期间微任务队列加入了新的微任务,会将新的微任务加入队列尾部,之后也会被执行。

简单理解,宏任务在下一轮事件循环执行,微任务在本轮事件循环的所有任务结束后执行。

  • 宏任务主要包括了:setTimeout、setInterval、setImmediate、I/O、各种事件(比如鼠标单击事件)的回调函数
  • 优先级:主代码块 > setImmediate > MessageChannel > setTimeout / setInterval
  • 微任务主要包括了:process.nextTick、Promise、MutationObserver
  • 优先级:process.nextTick > Promise > MutationObserver

据whatwg规范介绍:

  • 一个事件循环(event loop)会有一个或多个任务队列(task queue)
  • 每一个 event loop 都有一个 microtask queue
  • task queue == macrotask queue != microtask queue
  • 一个任务 task 可以放入 macrotask queue 也可以放入 microtask queue 中
  • 调用栈清空(只剩全局),然后执行所有的microtask。当所有可执行的microtask执行完毕之后。循环再次从macrotask开始,找到其中一个宏任务执行完毕,如果这个宏任务中可能包含宏任务或微任务,会将宏任务添加到事件队列中,然后再执行所有的microtask,这样一直循环下去。

宏任务、微任务执行流程图如下所示。

这时,我们再来看一下文章开头给的一段代码。

<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>

执行步骤如下所示。

  • 当页面首次加载时,<script>标签内的代码段作为一个宏任务进入主线程,依次向下执行。
  • 遇到setTimeout将回调函数注册后压入宏任务的事件队列。
  • 遇到new Promise立即执行,输出‘马上执行for循环了‘。将then函数压入到微任务队列。
  • 遇到console.log(‘代码执行结束‘),执行代码。输出"代码执行结束了"。
  • 主线程执行完后,先检查微任务队列中有没有待执行的任务,发现then函数在微任务队列里,将其取出到主线程执行,输出"执行then函数了"。
  • 开始下一轮事件循环,从红任务队列中取出setTimeout事件的回调函数,执行。输出“定时器开始了”。
  • 结束。

总结

javascript是一门单线程的语言,事件循环是js异步编程的一种方法。也是js的执行机制。当浏览器中的网页刚刚载入的时候,<script>里的代码会作为第一个宏任务被压入栈执行,同步代码执行完后,如果有微任务就执行微任务,没有微任务就执行下一个宏任务。如此往复循环,直至所有任务都执行完毕。

参考文章

JavaScript 运行机制详解:再谈Event Loop

浏览器内的事件队列

这一次,彻底弄懂 JavaScript 执行机制

原文地址:https://www.cnblogs.com/yuliangbin/p/9192558.html

时间: 2024-10-14 05:00:07

javascript的执行机制—Event Loop的相关文章

理解JavaScript的执行机制

一直没有深入了解过JavaScript的事件执行机制,直到看到了这篇文章:<这一次,彻底弄懂JavaScript执行机制> 才发觉熟悉JavaScript的执行机制非常重要. 毕竟在跟进项目中偶尔需要排查为什么会出现函数执行顺序不一样的情况. 感谢作者浅显易懂的文字让我获益匪浅,以下是自己对JavaScript执行机制的理解,全是流水账. 文章主要叙述: 1:单线程和异步任务 2: 异步任务的分类 3:setTimeout 和 setInterval 的执行方式 单线程和异步任务 JavaSc

JavaScript执行顺序Event Loop

javascript是一门单线程语言,为了实现主线程的不阻塞,但可以用Event Loop模拟多线程操作 Event Loop中同步异步任务执行顺序: 所有异步任务都是在Event Table中注册函数,当指定的时间完成时,Event Table会将函数放入Event Queue,主线程的同步任务执行完会去Event Queue读取对应函数,进入主线程执行. js引擎monitoring process进程,当发现主进程执行栈为空,会去执行Event Queue中的函数 let data = [

javascript基础修炼(5)—Event Loop

开发者的javascript造诣取决于对[动态]和[异步]这两个词的理解水平. 一. 一道考察异步知识的面试题 题目是这样的,要求写出下面代码的输出: setTimeout(() => { console.log(1) }, 0) new Promise((resolve, reject) => { console.log(2) for (let i = 0; i < 10000; i++) { i === 9999 && resolve() } console.log(

【JS】JavaScript引擎的内部执行机制

 近期在复习JavaScript,看到setTimeout函数时.想起曾经刚学时,在一本书上看过setTimeout()里的回调函数执行的间隔时间有昌不是后面设置的值.曾经没想太多.网上看了JS大神的解释,整理记录下JavaScript引擎的内部执行机制. 首先看一段小程序: <script> alert('第1'); setTimeout(function(){alert('第2');}, 2000); alert('第3'); </script> 输出顺序是:第1.第3,第

彻底弄懂 JavaScript 执行机制

本文的目的就是要保证你彻底弄懂javascript的执行机制,如果读完本文还不懂,可以揍我. 不论你是javascript新手还是老鸟,不论是面试求职,还是日常开发工作,我们经常会遇到这样的情况:给定的几行代码,我们需要知道其输出内容和顺序.因为javascript是一门单线程语言,所以我们可以得出结论: javascript是按照语句出现的顺序执行的 看到这里读者要打人了:我难道不知道js是一行一行执行的?还用你说?稍安勿躁,正因为js是一行一行执行的,所以我们以为js都是这样的: let a

JavaScript执行机制

原文 简书原文:https://www.jianshu.com/p/0d2d42fbe1dc 大纲 1.场景分析 2.执行机制相关知识点 3.以实例来说明JavaScript的执行机制 4.相关概念 1.场景分析 /* 以下这段代码的执行结果是什么? 如果依照:js是按照语句出现的顺序执行这个理念, 那么代码执行的结果应该是: //"定时器开始啦" //"马上执行for循环啦" //"执行then函数啦" //"代码执行结束"

这一次,彻底弄懂 JavaScript 执行机制

本文的目的就是要保证你彻底弄懂javascript的执行机制,如果读完本文还不懂,可以揍我. 文章转自:https://juejin.im/post/59e85eebf265da430d571f89 不论你是javascript新手还是老鸟,不论是面试求职,还是日常开发工作,我们经常会遇到这样的情况:给定的几行代码,我们需要知道其输出内容和顺序.因为javascript是一门单线程语言,所以我们可以得出结论: javascript是按照语句出现的顺序执行的 看到这里读者要打人了:我难道不知道js

JavaScript 执行机制

一.宏任务与微任务 macro-task(宏任务):包括整体代码script,setTimeout,setInterval micro-task(微任务):Promise,process.nextTick 二.JavaScript 执行机制 本文的目的就是要保证你彻底弄懂javascript的执行机制,如果读完本文还不懂,可以揍我. 不论你是javascript新手还是老鸟,不论是面试求职,还是日常开发工作,我们经常会遇到这样的情况:给定的几行代码,我们需要知道其输出内容和顺序.因为javasc

10分钟理解JS引擎的执行机制

10分钟理解JS引擎的执行机制 javascript 阅读约 7 分钟 深入理解JS引擎的执行机制 1.灵魂三问 : JS为什么是单线程的? 为什么需要异步? 单线程又是如何实现异步的呢? 2.JS中的event loop(1) 3.JS中的event loop(2) 4.说说setTimeout 首先,请牢记2点: (1) JS是单线程语言 (2) JS的Event Loop是JS的执行机制.深入了解JS的执行,就等于深入了解JS里的event loop 1.灵魂三问 : JS为什么是单线程的