事件循环
浏览器的事件处理机制&运行机制,称 EventLoop
为什么要有事件循环机制
JS 线程和渲染线程互斥切互相阻塞(同步获取 DOM 信息),为了避免 GUI 的卡顿,让异步操作同步执行,引入了浏览器事件循环机制 js 在单线程情况下非阻塞的调用网络,定时器等异步 API
什么是:运行机制
- 同步任务在主线程上执行,形成执行栈,先进后出
- 异步任务有了运行结果,就在任务队列之中放一个事件:形成任务队列, 先进先出,不同的异步任务会分配到不同的队列中
微任务&宏任务:
它分为微任务和宏任务,它放在任务队列中
- 任务源可以分为 微任务和宏任务
- 微任务包括 promise process.nextTick MutationObserver
- 宏任务 script setTimeout setInterval setImmediate I/O UI rendering requestAnimationFrame
他们是有先后顺序的
宏任务 - 微任务 - 渲染 - 宏任务 - 微任务 - 渲染
微任务 - 渲染 - 宏任务 - 微任务 - 渲染
首先执行同步代码,这属于宏任务
执行栈为空,查询是否有异步任务
执行微任务
如有必要渲染页面
下一轮
为什么区分微任务和宏任务
为了事情的优先级,宏任务一般耗时比较长,微任务耗时相对较短
setTimeout Promise
js
setTimeout(() => {
console.log(1);
}, 0);
Promise.resolve().then(() => {
console.log(2);
});
console.log(3);
// 3 2 1
// main script 运行结束后,会有微任务队列和宏任务队列。
// 微任务先执行,之后是宏任务。
setTimeout promise
js
setTimeout(() => {
console.log(1);
}, 0);
let a = new Promise((resolve) => {
console.log(2);
resolve(3);
})
.then(() => {
console.log(3);
return 3;
})
.then(() => {
console.log(4);
return 4;
});
console.log(5);
// 2 5 3 4 1
Promise 有 return
js
new Promise((resolve, reject) => {
console.log("promise1");
resolve(); // 异步让出 向下执行
})
.then(() => {
console.log("then11");
return new Promise((resolve, reject) => {
console.log("promise2");
resolve(); // 异步向下执行
})
.then(() => {
console.log("then21"); // 然后在then内部先执行 ,在resolve 异步让出向下
})
.then(() => {
console.log("then23");
});
})
.then(() => {
console.log("then12"); //
});
// promise1 -> then11 -> promise2 -> then21 -> then23 -> then12
promise 嵌套无 return
js
new Promise((resolve, reject) => {
console.log("promise1");
resolve();
})
.then(() => {
console.log("then11");
new Promise((resolve, reject) => {
console.log("promise2");
resolve();
})
.then(() => {
console.log("then21");
})
.then(() => {
console.log("then23");
});
})
.then(() => {
console.log("then12");
});
// promise1 -> then11 -> promise2 -> then21 -> then12 -> then23
多 promise [同层级,顺序为:从外到里]
js
new Promise((resolve, reject) => {
console.log("promise1");
resolve();
})
.then(() => {
console.log("then11");
new Promise((resolve, reject) => {
console.log("promise2");
resolve();
})
.then(() => {
console.log("then21");
})
.then(() => {
console.log("then23");
});
})
.then(() => {
console.log("then12");
});
new Promise((resolve, reject) => {
console.log("promise3");
resolve();
}).then(() => {
console.log("then31");
});
// promise1 -> promise3 -> then11 -> promise2 -> then31 -> then21 -> then12 -> then23
async await promise
js
async function async1() {
console.log("async1 start");
await async2();
console.log("async1 end");
}
async function async2() {
console.log("async2");
}
async1();
// 用于test的promise,看看await究竟在何时执行
new Promise(function (resolve) {
console.log("promise1");
resolve();
})
.then(function () {
console.log("promise2");
})
.then(function () {
console.log("promise3");
})
.then(function () {
console.log("promise4");
})
.then(function () {
console.log("promise5");
});
// async1 start -> async2 -> promise1 -> async1 end -> promise2 -> promise3 -> promise4 -> promise5
// async/await有时候会推迟两轮 microtask,
// 在第三轮 microtask 执行,主要原因是浏览器对于此方法的一个解析,由于为了解析一个 await,要额外创建两个 promise,因此消耗很大。
// 后来 V8 为了降低损耗,所以剔除了一个 Promise,并且减少了 2 轮 microtask,所以现在最新版本的应该是“零成本”的一个异步。
多 async
js
async function async1() {
console.log("async1 start");
await async2();
console.log("async1 end");
}
async function async2() {
console.log("async2");
}
console.log("script start");
setTimeout(function () {
console.log("settimeout");
});
async1();
new Promise(function (resolve) {
console.log("promise1");
resolve();
}).then(function () {
console.log("promise2");
});
console.log("script end");
// script start -> async1 start -> async2 -> promise1
// script end -> async1 end -> promise2 -> settimeout