一 进程与线程
线程是进程执行的最小单位,进程是系统分配任务的最小单位。
一个进程可执行最少一个线程。线程分为子线程和主线程。
主线程关闭则子线程关闭。
二 浏览器进程
浏览器是多进程多线程应用。
进程包括:
- 浏览器进程 负责程序交互
- 渲染进程 负责执行js等
- 网络进程 负责网络进程加载
渲染主线程负责执行js、vue,解释css、html等。
一个标签一个渲染进程。
渲染主线程执行最多次。
有线程将任务放到渲染主线程执行队列,渲染主线程执行队列从消息队列中获取任务执行。
同一类型任务在同一队列中。
消息队列包括:
- 微队列 执行优先级最高
- 交互队列 执行优先级中
- 延时队列 执行优先级低 执行回调
三 js异步理解
js为单进程语言。渲染主线程中执行js。
使用同步则可能导致渲染主进程堵塞。
渲染主线程执行队列其他任务无法执行。
浏览器采用异步方法可避免堵塞。
具体方法:
- 任务执行时,主线程将任务交给其他线程执行,主线程继续执行后续代码。
- 任务中回调函数,包装为任务,加入到延时队列中,待主进程执行。
以上方法最大限度保证单进程的流畅运行。
"主线程将任务交给其他线程执行"可以解释为,执行代码时,有线程将不同任务放到不同队列,根据队列执行优先级,主线程执行完全局任务后,获取任务执行。
交互事件任务进入交互队列。
Promise.resolve().then(function(){}) 将任务放入微队列。
三 js事件循环
渲染主线程循环执行,各个队列的任务,事件循环又叫消息循环。
类似于后端消息队列,有守护线程监听队列,其他线程执行对应数据,监听到有新数据就执行。
浏览器中开始死循环,执行消息队列中的任务,其他线程将任务插入队尾。
同类型的任务必须在同一队列,不同的任务应属于不同队列。
不同队列执行的优先级不同,微队列必须再存且执行优先级最高,其他队列优先级由浏览器自行决定。
四 js定时器是否精准
不精准
- 硬件没有原子钟
- 操作系统计时函数偏差,js计时执行操作系统函数
- 计时器实现时嵌套层级超过5层,则带有4毫秒时间差
- 受渲染主线程事件循环影响,计时器的回调在延时队列中,只能在渲染主线程空闲时运行,因此有偏差
原子钟为一个硬件设备,目前有芯片级原子钟。
国产芯片级微型原子钟:多领域应用,市场前景广阔!-电子发烧友网
五 示例
例:
const btn = document.getElementById("btn")
function clickf(event){
setTimeout(function(){
console.log("setTimeout1")
},0)
Promise.resolve().then(function(){
console.log("Promise1");
})
}
btn.addEventListener('click', clickf(event));
setTimeout(function(){
btn.click();
},0)
Promise.resolve().then(function(){
console.log("Promise2");
})
function sleep(times){
const date = Date.now();
let currentDate = null;
do {
currentDate = Date.now();
} while (currentDate - date < times);
}
function showaf(){
let a=1
console.log(a)
}
function showa(func){
if (typeof func === 'function') {
func(); // 执行回调函数
}
}
function settimeout1(){
console.log("setTimeout2")
Promise.resolve().then(function(){
console.log("Promise3");
})
console.log("setTimeout3")
}
setTimeout(settimeout1,0)
showa(showaf)
sleep(1000)
let b=2
console.log(b)
输出结果
调用顺序如下:
先是全局代码执行,依次推入延时队列任务1、微队列任务1、延时队列任务2。
根据推入顺序和队列的优先顺序,依次执行微队列任务1,延时队列任务1,延时队列任务2。
之后类推。
参考:
事件循环那点事_计算机硬件没有原子钟怎么理解-CSDN博客