eventloop学习记录- js同步异步执行顺序
浏览器是多线程的
浏览器常用的线程
- GUI渲染线程:渲染和解析界面
- JS 引擎线程: 渲染和解析JS [浏览器分配一个线程去解析js, 所以js是单线程]
- 定时器监听线程
- 事件监听线程
- http网络请求线程 【同源下,浏览器最多同时分配5~7个http线程】
JS 是单线程运行的,但是异步是如何实现的呢?
js 中的异步操作是借用浏览器的多线程机制, 在基于eventloop 事件循环机制 实现了单线程异步操作。
浏览器加载页面除了开辟堆、栈内存外,还创建了两个队列:
- webapi(任务监听队列-使用了浏览器的多线程去监听的)
- eventQueue(事件/任务队列)
首先:当主线程自上而下执行代码中, 如果遇到异步代码 会把异步任务放到webapi中去监听
(浏览器开启新的线程去监听是否可以执行,不会阻碍主线程的渲染,主线程继续往下执行代码)
其次:当异步任务被检测为可以执行了,也不会立即执行,会压入eventQueue排队等待主线程执行
- 根据微任务还是宏任务放在不同的队列中
- 谁先进来 谁排在各自队伍的最前边
PS. 对于定时器来讲 设定一个等待时间 到时间后并不一定立即执行。
最后:当同步代码执行完毕,主线程空闲下来,回去eventQueue中把正在排队的异步任务 按照顺序取出来执行
- 异步的微任务优先级比较高,无论其中的任务是先放入的还是后放入的 只要有可执行的异步微任务永远先执行他。
- 同样级别的任务 谁先放入的 先执行谁
- 是要把任务拿到栈中执行,而且交给主线程执行,所以只要这个拿出来的任务没有执行完,也不会去拿其他的任务。
异步宏任务有哪些,异步微任务有哪些?
- [异步微任务]
+ requestAnimationDrame
+ Promise.then/catch/finally
+ async/await
+ queueMicrotask 手动创建一个异步的微任务
+ MutationObserer
+ IntersectionObserver
+ … - [异步宏任务]
+ settimeout/setinterval
+ 事件绑定/队列
+ xmlhttpRequest/fetch
+ messageChannel
Promise和await运行过程的理解
1、Promise
- 情况1 p.then(onfulfilled, onreject) 已知实例p的状态和值, 也不会立即执行onfulfilled/onrejected。而是创建异步微任务【先进入webapi中发现状态是可以执行,再挪入eventqueue中等待执行】
- 情况2 如果不知道实例p的状态,则先把onfulfilled/onrejected储存起来【理解为放入webapi中去监听,只有知道其状态的时候才去执行。】resolve和reject执行之后 立即修改了实例的状态和值,也决定了webapi中监听的方法要执行哪一个,然后挪至eventqueue中,等待主线程中任务执行完毕,再将其拿出来执行。
2、await
- 立即执行await右边的方法,看其返回的promise实例
- 会把当前上下文中 await后边的代码当做异步的微任务
- 加入到webapi中进行监听要不要被执行
- 如果可以执行 进入eventQueue中等待被执行
举个例子
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' );
let body = document.body;
body.addEventListener('click', function() {
Promise.resolve().then(() => {
console.log(1)
})
console.log(2)
})
body.addEventListener('click', function() {
Promise.resolve().then(() => {
console.log(3)
})
console.log(4)
})
解释:
1、首先将click的handler函数依次加入webapi队列等待被触发
2、点击body后 依次触发两个handler,这时将两个函数放到了异步宏任务队列等待执行
3、js主线程空闲后,从异步宏任务队列中取出第一个handler进行执行
4、发现第一个handler中含有一个Promise.then(),此时会将.then()中的执行方法放入webapi队列,因为立马执行了resolve,所以console.log(1)会进入异步微任务队列,等待被执行
5、按顺序执行console.log(2),此时js主线程又进入空闲。
6、这时候异步微任务队列中有待执行的任务,会优先被执行。所以打印出1。此时js主线程空闲
7、微任务列表中没有任务才回去宏任务列表查看。发现第二个handler。开始执行。
8、重复4、5、6至此结束
所以经过应该是 2 1 4 3