事件循环(event loop)
执行顺序
执行顺序:同步任务 -> 异步任务(微任务 -> 宏任务)
异步任务:
宏任务: (放在callback queue中,由事件触发线程维护)
script(整体代码),
setTimeout, setInterval,
UI 渲染,
postMessage, MessageChannel,
I/O, setImmediate(Node环境)
微任务: (放在微任务队列中,由js引擎线程维护)
promise.then,
proness.nextTick(Node环境),
MutationObserver(监听DOM树变化)
事件循环-面试题1
console.log('js start');
async function async1(){
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2(){ console.log('async2'); }
setTimeout(function(){ console.log('timeout'); })
async1();
new Promise(function(resolve){
console.log('promise01');
resolve();
}).then(function(){
console.log('promise02');
})
console.log('js end');
1. console.log('js start'); // js start
宏任务队列:setTimeout
2. async1(); // async1 start
得到await右侧表达式的结果, 执行async2() // async2
3. 构造函数(同步):new Promise // promise01
微任务队列:.then
4. console.log('js end'); // js end
5. 同步代码完毕,回到async1()
await Promise.resolve(undefined) -> // async1 end
6. 微任务队列:.then // promise02
宏任务队列:setTimeout // timeout
- async 函数返回一个 Promise 对象,当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再执行函数体内后面的语句
事件循环-面试题2
console.log('a');
async function async1(){
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2(){ console.log('async2'); }
setTimeout(function(){
console.log('b')
queueMicrotask(function() { console.log('c'); })
new Promise(function(resolve) {
console.log('d')
resolve();
}).then(function() {
console.log('e');
})
})
async1();
queueMicrotask(function() { console.log('f'); })
new Promise(function(resolve) {
console.log('g')
resolve();
}).then(function() {
console.log('h');
})
setTimeout(function(){
console.log('i')
queueMicrotask(function() { console.log('j'); })
new Promise(function(resolve) {
console.log('k')
resolve();
}).then(function() {
console.log('l');
})
})
1. console.log('a'); // a
宏任务队列:setTimeout
2. async1(); // async1 start
得到await右侧表达式的结果, 执行async2() // async2
微任务: queueMicrotask
3. 构造函数(同步):new Promise // g
微任务队列:queueMicrotask, .then
宏任务队列:setTimeout(01), setTimeout(02)
5. 同步代码完毕,回到async1()
await Promise.resolve(undefined) -> // async1 end
6. 微任务队列:queueMicrotask, .then // f, h -> 微任务队列为空
7. 宏任务队列:setTimeout(01)
console,new Promise // b, d
微任务队列:queueMicrotask, .then // c, e -> 微任务队列为空
8. 宏任务队列:setTimeout(02)
console,new Promise // i, k
微任务队列:queueMicrotask, .then // j, l
事件循环-面试题3: promise`中并没有`resolve`或者`reject
const promise = newPromise((resolve, reject) => {
console.log(1);
console.log(2);
});
promise.then(() => {
console.log(3);
});
console.log(4);
// 1, 2, 4
- promise中并没有resolve或者reject,
- promise.then并不会执行,只有在被改变了状态之后才会执行
事件循环-面试题4
const promise1 = newPromise((resolve, reject) => {
setTimeout(() => {
resolve("success");
console.log("timer1");
}, 1000);
console.log("promise1里的内容");
});
// promise2是一个新的状态为pending的Promise
const promise2 = promise1.then(() => {
thrownewError("error!!!");
});
console.log("promise1", promise1);
console.log("promise2", promise2);
setTimeout(() => {
console.log("timer2");
console.log("promise1", promise1);
console.log("promise2", promise2);
}, 2000);
输出:
'promise1里的内容'
promise1 Promise {<pending>}
promise2 Promise {<pending>}
timer1
- resolve("success"); 将之前的promise1.then推入微任务队列,出了一个错误,且将promise2的状态设置为了rejected
Uncaught (in promise) ReferenceError: thrownewError is not defined
timer2
'promise1' Promise{<resolved>: "success"}
'promise2' Promise{<rejected>: Error: error!!!}
事件代理(事件委托)
事件传播分成三个阶段: 事件捕获 --> 事件目标 --> 事件冒泡
事件模型
- 捕获:从window对象传导到目标节点(上层传到底层),此阶段不会响应任何事件
-
目标阶段:在目标节点上触发
- 冒泡:点击子元素触发父元素事件(从底层传回上层),事件从目标元素上升到document
事件代理
- 需要为父元素的很多子元素添加事件时,可以通过把事件添加到父元素并把事件委托给父元素来触发事件处理函数,减少了DOM操作
-
事件代理即是利用事件冒泡的机制把里层所需要响应的事件绑定到外层
优点
1. 可以大量节省内存占用,减少事件注册,比如在ul上代理所有li的click事件
2. 可以实现当新增子对象时无需再次对其绑定(动态绑定事件)
不使用事件委托,那么新增或删除子节点就需要每次绑定或解绑节点事件
阻止冒泡 & 阻止默认行为
- 阻止冒泡:e.stopPropagation() | ie:e.cancelBubble = true
- 阻止默认行为:e.preventDefault() | return false | ie: returnValue = false;
- 事件源:点击者即触发者,e.target
- div.addEventListener('click', function(){},true); false 冒泡 | true捕获(ie不支持捕获)