• trigger click 和 click 的区别??


    trigger click 和 user click 有什么区别吗?

    好像没有的。直到发现了这样一段代码。

     <button class="btn1">Button</button>
    
        var btn1 = document.querySelector('.btn1');
        btn1.addEventListener('click', function () {
          Promise.resolve().then(function() {
            console.log('Microtask 1');
          })
    	    console.log('Listener 1');
        });
        btn1.addEventListener('click', function () {
          Promise.resolve().then(function() {
            console.log('Microtask 2');
          })
          console.log('Listener 2');
        });
    

    点击click按钮会出什么结果呢?

    Listener 1
    Microtask 1
    Listener 2
    Microtask 2
    

    那执行btn1.click()呢?

    Listener 1
    Listener 2
    Microtask 1
    Microtask 2
    

    很神奇。

    先来一个简单的问题

    以下代码输出什么呢?

    console.log('start')
    setTimeout(()=>{
        console.log('setTimeout')
    }, 0)
    Promise.resolve().then(function() {
        console.log('promise1')
    }).then(function() {
        console.log('promise2')
    })
    console.log('end')
    

    为什么会是这个顺序呢?先来个图讲解一下。

    js的事件循环

    看标题就知道原因就是事件循环了。
    js是单线程驱动,执行的包含主线程(stack)和任务队列两部分,主线程负责处理同步任务,异步任务和回调会加到任务队列里面,每当主线程完成前一个任务,回调函数就会在一个无限循环圈里被调用,因此这个圈被称为事件循环。回调函数正在等待轮到自己执行所排的队就被称为任务队列。
    还是没说到回调和异步的执行顺序?

    macrotask 和 microtask

    macrotask 和 microtask就是两个任务队列。举个例子:
    macrotasks的操作 setTimeout setInterval setImmediate I/O UI渲染
    microtasks的操作 Promise process.nextTick Object.observe MutationObserver
    可以简单的理解为异步操作会加到macrotask队列,回调等监听操作会加到microtasks里。
    那执行顺序是什么?
    首先js会执行主线程(stack)的代码,就是同步的代码。这时候遇到异步操作怎么办?加到macrotask队列里准备执行回调加到 microtasks里准备执行。
    主线程(stack)为空的时候,开始执行任务队列的代码。这是就开始了一个事件循环。
    简单来说就3步:

    1. 在 macrotask 队列中执行最早的那个 task ,然后移出
    2. 执行 microtask 队列中所有可用的任务,然后移出
    3. 下一个循环,执行下一个 macrotask 中的任务 (再跳到第2步)
      microtask queue 中的 task总是在当前的循环执行。

    一份伪代码:

    for (macroTask of macroTaskQueue) {
        // 1. Handle current MACRO-TASK
        handleMacroTask();
        // 2. Handle all NEXT-TICK
        for (nextTick of nextTickQueue) {
            handleNextTick(nextTick);
        }
        // 3. Handle all MICRO-TASK
        for (microTask of microTaskQueue) {
            handleMicroTask(microTask);
        }
    }
    

    每次执行macroTask的时候都会清空一下 microTask。

    回到简单的问题。

    这是我们再去看一下这个解释的gif。
    console是同步任务,总是入stack,然后同步执行完。执行的过程settimeout会加到macrotask里,回调加到microtask里。
    console完成了,stack为空,开始执行microtask,执行microtask的时候可以继续增加microtask,就像2个then。都执行完之后再执行macrotask的settimeout。

    注意,run script 也是一个macrotask,所以执行stack的时候就是在执行第一个macrotask。所以当stack为空会先执行一次microtask。

    回到code trigger和user trigger

    dalao们应该已经知道为什么有区别了,其实就是macrotask和microtask的问题。
    code trigger的时候,stack盏不是空的,所以两个click的microtask会顺序压入盏中,没有中断js的执行,当执行到btn1.click()的时候,stack空,顺序执行microtask。

    推荐一篇相关文章,有图形化的讲解,特别清楚。click me

    requestanimationframe

    requestanimationframe是一个比较特殊的api,对于它,浏览器只保证requestAnimationFrame的回调在重绘之前执行,没有确定的时间,何时重绘由浏览器决定。
    所以有时候一样的代码执行结果不一样。
    requestanimationframe属于macrotask。

    拓展

    async的异步调用

    async的异步调用,其实还是promis的调用。看一下例子:

    async function async1() {
        console. log('async1 start' );
        await async2();
        console.log('async1 end' );
    }
    async function async2() {
        console.log('async2');
    }
    console.log('script start' );
    async1();
    new Promise(function(resolve) {
        console.log('promise1');
        resolve();
    }).then(function() {
    console.log('promise2');
    });
    console.log('script end');
    

    这个输出什么呢?
    为什么async2在promise2前面呢?
    看一下这个理解一下->
    (来自阮一峰es6 - async的实现原理)

    async function fn(args) {
      // ...
    }
    
    // 等同于
    
    function fn(args) {
      return spawn(function* () {
        // ...
      });
    }
    
    function spawn(genF) {
      return new Promise(function(resolve, reject) {
        const gen = genF(); // 对应 console. log('async1 start' ),console.log('async2');
        function step(nextF) {
          let next;
          try {
            next = nextF();
          } catch(e) {
            return reject(e);
          }
          if(next.done) {
            return resolve(next.value);
          }
          Promise.resolve(next.value).then(function(v) {
            step(function() { return gen.next(v); });
          }, function(e) {
            step(function() { return gen.throw(e); });
          });
        }
        step(function() { return gen.next(undefined); });
      });
    }
    

    那么了解之后再看看这个?

    async function async1() {
        console. log('async start' );
        await console.log('async2');
        await console.log('async3');
        await console.log('async4');
        console.log('async end' );
    }
    
    console.log('script start' );
    async1();
    new Promise(function(resolve) {
        console.log('promise1');
        resolve();
    }).then(function() {
        console.log('promise2');
    });
    console.log('script end');
    

    思考一下为什么async4,5之后的await都在promise2之前呢??

  • 相关阅读:
    小注意1
    javascript求相对路径
    js网页返回顶部和楼层跳跃的实现原理
    函数传值的学习笔记
    每日一题——归并排序
    python文件处理
    Docker数据卷
    Docker镜像原理
    Docker可视化之Portainer
    Docker部署Nginx、Tomcat
  • 原文地址:https://www.cnblogs.com/dh-dh/p/8514792.html
Copyright © 2020-2023  润新知