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

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

trigger click 和 click 的区别??

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

上一篇:WPFの命中测试


下一篇:【Python基础】lpthw - Exercise 40 模块、类和对象