• js 宏任务和微任务


    写在前面:压力只是暂时的,都会过去,这是我一周以为听到的最顿悟的一句话了吧~

      1.引言

      js作为单线程的运行机制,必定有自己的运行顺序,在听了一次分享后,也好奇这种运行的机制到底是什么?

      js可分为同步任务和异步任务,对于同步的任务,我们当然知道按照顺序进行执行,但是对于异步的操作,会有一个优先级的执行顺序,分别为宏任务和微任务

    宏任务(macrotasks)和微任务(microtasks)??包含什么?

        macrotasks: setTimeout, setInterval, setImmediate, I/O, UI rendering
        microtasks: process.nextTick, Promises, Object.observe(废弃), MutationObserver
    

     在js执行时候,一个主线程里面都会有一个事件循环(消息循环|运行循环)和事件队列,存放各种要处理的事件信息,通过这个循环不断处理这些事件信息或消息。

    谈到这里,很明显知道,其实出现宏任务和微任务和浏览器以及js的执行机制有很大的关系。了解js的执行机制

    2.javascript的执行runtime

     这是一张javascript的执行机制图

    JavaScript的运行分为

      (1) javaScript Engine,Chrome 的引擎就是 V8

      (2) Web APIs,DOM 的操作,AJAX,Timeout 等实际上调用的都是这里提供的

      (3)Callback Queue,回调的队列,也就是刚刚所有的 Web APIs 里面的回调函数,实际上都是放在这里排队的

      (4) EventLoop,事件循环,也就是刚所说的宏任务和微任务的容器

    call Stack

          本身就是一个调用栈(就像浏览器中的JavaScript解释器),追踪函数执行流的一种机制,当执行环境调用了多个函数时,通过调用栈,我们可以追踪到哪一个函数在执行,执行的函数体中又调用了哪些函数。

                 每调用一个函数,解释器就会把该函数添加进调用栈并开始执行。

                正在调用栈中执行的函数还调用了其它函数,那么新函数也将会被添加进调用栈,一旦这个函数被调用,便会立即执行。

                 当前函数执行完毕后,解释器将其清出调用栈,继续执行当前执行环境下的剩余的代码。

                 当分配的调用栈空间被占满时,会引发“堆栈溢出”。

        是存放执行的重要条件,也是因为只有一个调用栈,所以被称为单线程

    callback quene

       在js的编译阶段,将一些时间防止在执行队列中

    EventLoop 事件循环

        一个作用就是将callback quene队列里的执行事件放在在call stack中,执行

    3.事件循环

      js是单线程的,执行较长的js时候,页面会卡死,无法响应,但是所有的操作都会被记住到另外的队列。比如:点击了一个元素,不会立刻的执行,但是等到js加载完毕后就会执行刚才点击的操作,能够知道有一个队列记录了所有有待执行的操作,这个队列分为微观和宏观。微观会比宏观执行得更快。

       事件循环就是多线程的一种工作方式,Chrome里面是使用了共享的task_runner对象给自己和其它线程post task过来存起来,用一个死循环不断地取出task执行,或者进入休眠等待被唤醒。Mac的Chrome渲染线程和浏览器线程还借助了Mac的sdk Cococa的NSRunLoop来做为UI事件的消息源。Chrome的多进程通信(不同进程的IO线程的本地socket通信)借助了libevent的事件循环,并加入了到了主消息循环里面。

      称为事件循环的原因大多来源于源码

    while (queue.waitForMessage()) {
      queue.processNextMessage();
    }

      可以看到入伏哦有消息就会执行,没消息就会继续等待

    消息执行完成
    由于js单线程,每一个消息完整的执行后才会执行其他的消息,不会被其他的消息抢占,这样的缺点就是当一个消息执行的特别久的时候web用户无法处理用户的交互,比如点击或者滚动等,因此需要缩短消息的处理时间,将一个消息裁剪成多个消息。
    添加消息
    在浏览器里,当一个事件发生且有一个事件监听器绑定在该事件上时,消息会被随时添加进队列。如果没有事件监听器,事件会丢失。所以点击一个附带点击事件处理函数的元素会添加一个消息,其它事件类似。比如事件委托,监听,可随时放入消息队列
    setTimeout 接受一个参数,待加入队列的消息和一个延迟,延迟代表了消息加入队列的最小延迟事件,如果队列中没有其他的消息,在这段延迟时间过了后,消息会被立刻处理,但是如果有其他的消息,必须等到其他的消息处理完,因此,延迟时间表示最少延迟时间
     

    4.宏任务和微任务 

      是一种人们定义的执行名字,

    如何区分宏任务和微任务呢?划分的标准是什么

    宏任务本质:参与了事件循环的任务。
    回到 Chromium 中,需要处理的消息主要分成了三类:
    • Chromium 自定义消息
    • Socket 或者文件等 IO 消息
    • UI 相关的消息
    1. 与平台无关的消息,例如 setTimeout 的定时器就是属于这个
    2.Chromium 的 IO 操作是基于 libevent 实现,它本身也是一个事件驱动的库
    3.UI 相关的其实属于 blink 渲染引擎过来的消息,例如各种 DOM 的事件
    其实与 JavaScript 的引擎无关,都是在 Chromium 实现的。

    微任务本质:直接在 Javascript 引擎中的执行的,没有参与事件循环的任务。
    1. 是个内存回收的清理任务,使用过 Java 的童鞋应该都很熟悉,只是在 JavaScript 这是V8内部调用的
    2. 就是普通的回调,MutationObserver 也是这一类
    3. Callable
    4. 包括 Fullfiled 和 Rejected 也就是 Promise 的完成和失败
    5. Thenable 对象的处理任务
    宏任务,微任务的优先级
    promise 是在当前脚本代码执行完后,立刻执行的,它并没有参与事件循环,所以它的优先级是高于 setTimeout。
    宏任务和微任务的总结:
    • 宏任务 Macrotasks 就是参与了事件循环的异步任务。
    • 微任务 Microtasks 就是没有参与事件循环的“异步”任务。
    • console.log("开始执行1")
      console.log(Object.keys({a: 1}));
      setTimeout(() => {
          console.log(Object.keys({b: 2}));
          var promise = new Promise((resolve, reject) => {
              resolve(1);
          });
          promise.then(res => {
              //后续增加测试
              setTimeout(() => {
                  console.log(Object.keys({e:1}))
              }, 0);
              console.log(Object.keys({c: 1}));
          });
      }, 2000);
      console.log("结束执行2")
      

        

       微观任务是在当前JS调用执行完了之后立刻执行的,是同步的,在同一个调用栈里,没有多线程异步,如这里包括promise.then在内的setTimeout回调里的代码都是在DOMTimer.Fired执行的,只是说then被放到了当前要执行的整一个异步回调函数的最后面执行。所以setTimeout 0是给主线程的消息循环任务队列添加了一个新的task(回调),而promise.then是在当前task的V8里的microtask插入了一个任务。那么肯定是当前正在执行的task执行完了才执行下一个task.vue的nextTick 也是一个微观任务

    除此之外的onload事件
    let img = new Image(); 
    img.src = 'image01.png?_=' + Date.now(); 
    img.onload = function () { console.log('img ready'); } console.log(Object.keys({e: 1}));
    微观任务是不属于事件循环的,它是V8的一个实现,用来实现Promise的then/reject,以及其它一些需要同步延后的callback,本质上它和当前的V8调用栈是同步执行的,只是放到了最后面。除了Promise/MutationObserver,在JS里面发起的请求也会创建一个微观任务延后执行。
    参考网站:

  • 相关阅读:
    页面小标签
    mysql 给表和字段加注释
    jackson中的@JsonBackReference
    spring boot 学习
    bootstrapTable 学习使用
    $.ajax()方法详解
    2020全新出发,DevExpress WPF 计划发布功能蓝图—Part 5
    Web UI开发神器—Kendo UI for jQuery数据管理之搜索/分页功能
    Winforms界面开发小技巧揭秘!DevExpress 自动建议功能
    Themes、Windows UI控件新玩法—DevExpress WPF v19.2
  • 原文地址:https://www.cnblogs.com/mfyngu/p/11747533.html
Copyright © 2020-2023  润新知