「这是我参与11月更文挑战的第25天,活动详情查看:2021最后一次更文挑战」。
JS事件循环机制
进程和线程
- 浏览器打开一个页面就相当于开一个进程,在进程中,我们会同时做很多事情,每一个事情都有一个“线程”去处理,所以一个进程中可能会包含多个线程!!
- 浏览器是多线程的: GUI渲染线程:渲染页面 & 绘制图形 JS引擎线程:渲染和解析JS代码 事件触发线程:监听事件触发 定时触发器线程:给定时器计时的 异步HTTP请求线程:基于HTTP网络从服务器端获取资源和信息 WebWorker ...
- 同时做多件事情是“异步编程”; 一次只能处理一件事情,上一件事情处理完,下一件才能开始处理,这种操作是“同步编程”
异步编程实现的机制
- 多线程机制
- EventLoop事件循环机制
- ...
- JS是单线程的(浏览器只分配一个线程“JS引擎线程”用来渲染和解析JS代码)
-
JS中的大部分代码操作都是“同步”
-
有少部分操作,结合Eventloop机制,实现了“异步”处理
[异步宏任务:macrotask]
- 定时器:setTimeout/setInterval
- 事件绑定/队列
- 数据请求:Ajax/Fetch
- MessageChannel
- setImmediate[NODE]
- ... [异步微任务:microtask]
- Promise.then/catch/finally
- async/await
- queueMicrotask
- MutationObserver
- IntersectionObserver
- requestAnimationFrame
- process.nextTick[NODE]
- ...
-
JS事件循环解析
console.log(1);
setTimeout(() => {
console.log(2);
}, 0);
console.log(3);
for (let i = 0; i < 99999999; i++) {} //100MS左右
console.log(4);
setTimeout(() => {
console.log(1);
}, 20);
console.log(2);
setTimeout(() => {
console.log(3);
}, 10);
console.log(4);
for (let i = 0; i < 90000000; i++) {}
console.log(5);
setTimeout(() => {
console.log(6);
}, 8);
console.log(7);
setTimeout(() => {
console.log(8);
}, 15);
console.log(9);
promise的使用总结
let p1 = new Promise((resolve, reject) => {
console.log(1)
resolve(100)
console.log(2)
})
console.log(p1) //fulfilled 100
//p1.then 如果已知promise实例的状态,我们就把执行的方法onfulfilled或者onrejected放在EventQueue的异步微任务队列中等待执行[并不会立即执行方法,也即是then其实是异步的任务]
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(100) //立即更新实例的状态和 值,但是通知之前存储的方法执行这个操作是“异步”的
console.log(2)
},1000)
})
console.log(p1) //pending
//如果此时还不知道p1实例的状态,则我们会把onfulfilled/onrejected存储在一个容器中,等到后期我们基于resolve/reject把实例状态修改后,则在通知之前存放的方法执行【但是也不是立即执行,而是放在异步的微任务等待队列中】
p1.then(result => {
console.log(`成功:${result}`);
}, reason => {
console.log(`失败:${reason}`);
});
console.log(3);
// 3 2 成功:100
// 基于then返回的promise实例,它的状态和值,主要看onfulfilled/onrejected执行
// - 函数返回的不是promise实例:方法执行不报错,p2状态是成功,值是返回值;方法执行报错,则p2是失败的,值是报错原因
// - 函数返回的是个promise实例:则这个实例的状态和值决定了p2的状态和值
let p1 = Promise.resolve(100);
console.log(p1); //fulfilled
let p2 = p1.then(result => { //p1.then的时候,此处的onfulfilled方法放在EventQueue中等待执行(@A)
console.log(`成功:${result}`);
return 1000;
});
console.log(p2); //pending
p2.then(result => { //此时还不知道p2的状态,所以先把onfulfilled存储起来(@B)
console.log(`成功:${result}`);
});
console.log('SYNC END');
//同步结束后开始执行@A -> 成功:100并且修改p2的状态是成功,值是1000;
//此时才知道@B可以执行了,把其也放在等待的异步微任务队列中 -> 如果没有其他的异步任务执行,这把@B也拿出来执行 -> 成功:1000
async/await使用总结
const query = () => {
return new Promise(resolve => {
setTimeout(() => resolve(300),1000)
})
}
(async () => {
//如果await后面放置的不是一个Promise实例,则浏览器默认会把其转换为一个“状态为成功,值就是await后的值”的promise实例
// - await需要等待后面的promise实例是状态为成功的,才会执行下面的代码
// - 首先当前上下文中,await下面的代码都是异步的微任务 @aw
// - 如果已经知道await后面的实例状态是成功的,则 @aw 直接放在EventQueue中,等待执行即可
// - 如果后面实例状态是失败的,则 @aw 在webapi中永远不会进入到EventQueue中,因为永远不会执行
// - 如果暂时还不知道是成功还是失败,则@aw 先放置在webapi中,等到知道实例状态是成功后,在挪至到EventQueue中等待执行
let result = await 1 //await Promise.resolve(1)
console.log(result);
result = await Promise.resolve(2)
console.log(result)
result = await querey();
// 先把query执行,把返回的promise实例放在await后面等着,当前案例只有1000ms后,才能知道实例状态
})()