zoukankan      html  css  js  c++  java
  • 浏览器中js执行机制学习笔记

    浏览器中js执行机制学习笔记

    0.0772019.05.15 20:56:37字数 872阅读 291

    同步任务

    当一个脚本第一次执行的时候,js引擎会解析这段代码,并将其中的同步代码按照执行顺序加入执行栈中,然后从头开始执行。如果当前执行的是一个方法,那么js会向执行栈中添加这个方法的执行环境,然后进入这个执行环境继续执行其中的代码。当这个执行环境中的代码 执行完毕并返回结果后,js会退出这个执行环境并把这个执行环境销毁,回到上一个方法的执行环境。这个过程反复进行,直到执行栈中的代码全部执行完毕。

     
    call stack

    这个同步代码的执行过程可以是无限进行下去的,除非发生了栈溢出,即超过了所能使用内存的最大值。

    异步任务

    js引擎遇到一个异步事件后并不会一直等待其返回结果,而是会将这个事件挂起,继续执行执行栈中的其他任务。当一个异步事件返回结果后,js会将这个事件加入与当前执行栈不同的另一个队列,我们称之为事件队列。被放入事件队列不会立刻执行其回调,而是等待当前执行栈中的所有任务都执行完毕, 主线程处于闲置状态时,主线程会去查找事件队列是否有任务。如果有,那么主线程会从中取出排在第一位的事件,并把这个事件对应的回调放入执行栈中,然后执行其中的同步代码...如此反复

     
    image.png

    事件队列

    我们先看两个实验

    // 实验一
    console.log('进入页面')
      
    setTimeout(() => {
        console.log('setTimeout1')
    }, 0)
    new Promise((resolve) => {
        console.log('promise')
        setTimeout(() => {
          console.log('setTimeout2')
          resolve()
        }, 0)
    }).then(() => {
        console.log('then')
    })
    console.log('代码执行结束')
    
    /* 打印结果
    进入页面
    promise
    代码执行结束
    setTimeout1
    setTimeout2
    then
    */
    

      

    // 实验二
    console.log('进入页面')
      
    setTimeout(() => {
        console.log('setTimeout1')
    }, 0)
    new Promise((resolve) => {
        console.log('promise')
        resolve()
    }).then(() => {
        console.log('then')
    })
    console.log('代码执行结束')
    
    /*打印结果
    进入页面
    promise
    代码执行结束
    then
    setTimeout1
    */
    

      

    上面的两个demo中,demo2里在Promise里使用了setTimeout包了一层。造成then方法中的回调函数执行顺序稍有不同。一个在setTimeout之后,一个在setTimeou之前。为什么会造成这样的结果?

    因为事件分为 macro-taskmicro-task。不同的事件会进入不同的队列。
    也就是说事件队列对应的也有两个,macro queue和 micro queue

    • macro-task(宏任务):包括整体代码script,setTimeout,setInterval
    • micro-task(微任务):Promise,process.nextTick

    上文中提到当主线程闲置后,会去事件队列中查找执行回调函数,这个是有一个先后策略的,先micro queuemacro queue。而这里的promise中只有在执行了resolve()后才会向micro queue中push在then方法里写的回调函数。这也是上边两个实验中console.log('then')执行顺序不同的原因,下面是详细的执行顺序:

    实验1:
    // 主线程开始执行同步代码
    进入页面
    //遇到setTimeout1异步挂起,接着执行同步代码
    promise
    //遇到setTimeout2异步挂起,接着执行同步代码
    代码执行结束
    // 同步代码执行完毕,主线程闲置,检查micro queue为空,检查macro queue 有两个回调函数
    setTimeout1
    setTimeout2
    //执行了resolve(),向micro queue中push回调函数
    //同步代码执行完毕,主线程闲置检查micro queue,执行里边的回调函数
    then
    

      

    实验2:
    // 主线程开始执行同步代码
    进入页面
    //遇到setTimeout1异步挂起,接着执行同步代码
    promise
    //执行了resolve(),向micro queue中push回调函数
    代码执行结束
    // 同步代码执行完毕,主线程闲置,检查micro queue发现有回调函数执行
    then
    // 同步代码执行完毕, 主线程闲置, 检查micro queue为空,检查macro queue发现有回调函数并执行
    setTimeout1
    

      

    总结

    不同类型的任务会进入对应的Event Queue,比如setTimeout和setInterval会进入相同的Event Queue。

    事件循环的顺序,决定js代码的执行顺序。进入整体代码(宏任务)后,开始第一次循环。接着执行所有的微任务。然后再次从宏任务开始,找到其中一个任务队列执行完毕,再执行所有的微任务。

     
  • 相关阅读:
    BeanFactory 工厂模式
    中小型企业架构
    数据状态图
    好文章
    leetcode 最受欢迎的91道题目
    windows下安装mysql8并修改密码
    leetcode 1049 Last Stone Weight II(最后一块石头的重量 II)
    leetcode 910. Smallest Range II
    leetcode 908. Smallest Range I
    leetcode 900. RLE Iterator
  • 原文地址:https://www.cnblogs.com/chargeworld/p/12044579.html
Copyright © 2011-2022 走看看