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之前呢??


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM