前言
Event Loop
即事件循环 是指浏览器或Node的一种解决js单线程运行时不会阻塞的一种机制 也就是我们经常使用异步的原理 为了实现js的异步概念
- 进程: 计算机分配任务的最小单位
- 线程: 进程里包含多个线程
EventLoop
解决的是 js执行时可能会调用异步方法 这些方法是怎样调度执行的
浏览器的进程
- 每一个tab页都是进程 (tab页互不影响)
- 浏览器也有一个主进程 (用户界面)
- 网络进程 (处理请求)
- GPU进程 3d 绘制
- 第三方插件的进程
渲染进程
每个tab页里 都有一个渲染进程 (浏览器内核)
渲染进程
- 包含着多个线程
- GUI渲染线程 用来渲染页面
- JS引擎线程 他和页面渲染时互斥
- 事件触发线程 独立的线程
EventLoop
- 事件
click
setTimeout
ajax
也是一个独立线程
流程
- 如下图示
- 宏任务 如:
setTimeout setInterval postMessage MessageChannel setImmediate...
- 微任务 如:
promise.then mutationObserver
* js执行的时候 会从上到下执行
* 遇到函数会创建执行上下文 放入到执行栈中
* 执行完毕后会出栈 执行时可能会发生异步事件(内部会调用浏览器Api 如: `setTimeout`, `ajax`...)
* 当我们执行上下文栈都执行完毕后 等会可能api(如 `setTimeout`)执行完成或者时间到达
* 会被维护到一个事件队列中 (原则先进先出)
* 不停的扫描队列 将队列里的任务拿出来放到上下文栈中执行
* 事件循环线程是专门干这件事的 检测当前执行栈是否为空 如果为空 从事件队列中取出**一个来执行** (如`setTimeout(宏任务)`)
* 当代码执行时还会有一些任务 以`promise.then(微任务)`为例
* 每次执行宏任务的时候 都会单独创建一个 微任务队列 (原则先进先出)
* 微任务在执行完毕后 浏览器会检测是否要重新渲染 浏览器有刷新频率 大约16.6ms
* 小于这个时间 浏览器是不会渲染的
* 每次循环一次都会执行一个宏任务 并清空对应的微任务队列
* 微任务中在执行时再生成微任务 会在本轮直接清空
* 每次循环完毕后 都要看是否要渲染 如果需要渲染才渲染
示例
微任务和GUI渲染
// 页面的背景色 有没有从红到黄
// 执行栈 [console.log(1) console.log(3)]
// 微任务队列 [Promise.then]
// 执行完宏任务 清空微任务
// 再去渲染 页面没有从红到黄, 直接就是黄色
// log: 1 3 2
document.body.style.background = 'red';
console.log(1)
Promise.resolve().then(()=>{
console.log(2)
document.body.style.background = 'yellow';
})
console.log(3);
事件任务
<button id="button">事件按钮</button>
// 第一种
// 当前未点击button按钮 直接在下 button.click() 输出的结果是什么
// 当前两个事件在执行栈中执行
// 执行栈 ['click1' 'click2']
// 微任务队列 [Promise.then1 Promise.then2]
// log: 'click1' 'click2' 'c1-micro-task1' 'c2-micro-task2'
button.addEventListener('click',()=>{ // 1
console.log('click1');
Promise.resolve().then(()=>console.log('c1-micro-task1'))
})
button.addEventListener('click',()=>{ // 2
console.log('click2');
Promise.resolve().then(()=>console.log('c2-micro-task2'))
})
button.click();
// 第二种
// 当前点击button按钮 输出的结果是什么
// 上面流程我们说道
// 事件循环线程是专门干这件事的 检测当前执行栈是否为空 如果为空 从事件队列中取出**一个来执行**
// 一个一个拿到执行栈执行
// 宏任务队列 [addEventListener1 addEventListener2]
// 先拿出 addEventListener1, 并清空微任务队列 [Promise.then1]
// 再拿出 addEventListener2, 并清空微任务队列 [Promise.then2]
// log: 'click1' 'c1-micro-task1' 'click2' 'c2-micro-task2'
button.addEventListener('click',()=>{ // 1
console.log('click1');
Promise.resolve().then(()=>console.log('c1-micro-task1'))
})
button.addEventListener('click',()=>{ // 2
console.log('click2');
Promise.resolve().then(()=>console.log('c2-micro-task2'))
})
定时器任务
// 执行栈执行 有定时器 时间以到 放到 宏任务队列 [setTimeout2]
// 执行栈执行完
// 接着清空微任务队列[Promise.then1]
// 微任务有定时器 时间以到 放到 宏任务队列 [setTimeout2 setTimeout1]
// 最后一个一个宏任务 执行
// log: Promise1 setTimeout2 Promise2 setTimeout1
Promise.resolve().then(() => { // 1
console.log('Promise1')
setTimeout(() => {
console.log('setTimeout1')
}, 0);
})
setTimeout(() => { // 2
console.log('setTimeout2');
Promise.resolve().then(() => {
console.log('Promise2')
})
}, 0);
一道有意思的面试题
await
会暂停async
函数内后面的代码
- 先执行
async
函数外的同步代码
- 如果
await
的是 Promise
对象
- 等着
Promise
对象 的状态是status = 'FULFILLED'
- 然后把
resolve
的参数作为 await
表达式的运算结果返回后
- 再继续执行
async
函数内后面的代码
// log: 1 6 2 3 8 7 4 5
console.log(1);
async function async () {
console.log(2);
await console.log(3);
console.log(4)
}
setTimeout(() => {
console.log(5);
}, 0);
const promise = new Promise((resolve, reject) => {
console.log(6);
resolve(7)
})
promise.then(res => {
console.log(res)
})
async ();
console.log(8);
完
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
- 找不到素材资源介绍文章里的示例图片?
- 对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
- 模板不会安装或需要功能定制以及二次开发?
- 请QQ联系我们
发表评论
还没有评论,快来抢沙发吧!