消息队列:数据结构,存放要执行的任务,先进先出
线程模型:
- 第一版:单线程按照顺序处理确定好的任务
- 第二版:引入循环语句和事件系统,解决线程执行过程中接受并处理新任务
- 第三版:引入消息队列,接受其他线程发送来的任务
- 第四版:引入IO线程,通过IPC把任务发给IO线程,然后再把任务发送给页面主线程
消息队列中任务类型:
- 内部消息:鼠标事件、微任务、文件读写、websocket、javascript定时器等(主线程)
- 页面相关:js执行、解析dom、样式计算、布局计算、css动画等(主线程)
页面单线程的缺点
-
如何处理高优先级任务:微任务权衡效率和实时性
-
消息队列里的任务称为宏任务,宏任务中又包含了一个微任务队列
-
如何解决单个任务执行时间过长:回调
setTimeout
function showName(){
console.log("掘金社区")
}
var timerID = setTimeout(showName,200);
// timerID -> 整数,定时器编号,可以通过编号来取消定时器
// 定时器如果没有别执行,可以用clearTimeout 取消
chrome 消息队列
- 正常
- 延迟执行的任务列表:setTimeout
定时器问题
-
当前代码执行过久会影响定时器任务,实时性要求高(动画)的需求不适合
function bar() { console.log('bar') } function foo() { setTimeout(bar, 0); for (let i = 0; i < 5000; i++) { let i = 5+8+8+8 console.log(i) } } foo()
//定时器 500ms 后执行
-
setTimeout 嵌套调用,最短间隔时间4ms
function cb() { setTimeout(cb, 0); } setTimeout(cb, 0);
// chrome 中 定时器嵌套调用5次以上,系统判断该函数被阻塞,系统最短间隔时间4ms
-
未激活页面,setTimeout 执行最小间隔 1000ms
-
延时执行时间有最大值:24.8天,超过这个时间,按照0设置
-
setTimeout 中的 this 指向全局,可以用箭头函数避免
XMLHttpRequest
xhr 问题
- 跨域
- https 混合内容
宏任务 & 微任务
宏任务:主线程-> 消息队列里的任务,通过事件循环系统执行,时间颗粒度大,执行间隔不能精确控制
- 渲染事件(解析DOM、计算布局、绘制)
- 用户交互事件(鼠标点击、滚动页面、放大缩小)
- js 脚本执行事件
- 网络请求,文档读写
- setTimeout
微任务:异步执行的函数,在主函数执行结束后,当前宏任务结束之前
- DOM 节点变化产生的微任务(MutationObserver 异步解决性能、微任务解决实时性)
- Promise 产生的微任务
Promise
回调函数延迟绑定+回调函数返回值穿透->解决循环嵌套
function Bromise(executor) {
var onResolve_ = null
var onReject_ = null
//模拟实现resolve和then,暂不支持rejcet
this.then = function (onResolve, onReject) {
onResolve_ = onResolve
};
function resolve(value) {
setTimeout(()=>{
// 实际情况 是放到微任务里
onResolve_(value)
},0)
}
executor(resolve, null);
}
// Bromise 来实现我们的业务代码
function executor(resolve, reject) {
resolve(100)
}
//将Promise改成我们自己的Bromsie
let demo = new Bromise(executor)
function onResolve(value){
console.log(value)
}
demo.then(onResolve)
async/await
在不阻塞主线程的情况下,用同步代码实现异步访问资源,使逻辑代码更清晰
进程 > 线程 > 协程
进程上可以有多个并行的线程,线程(cpu 调度)切换需要消耗资源
线程上可以有多个协程,但同时只能执行一个,由程序控制,不归操作系统内核管,性能提升
生成器函数(一种协程的实现形式):带*函数,yield 暂停,next恢复执行
async:通过异步执行并隐式返回 Promise 作为结果的函数 = 生成器 + promise
awit:执行时创建一个Promise 对象