同步和异步
+ 同步 sync:事情必须一件一件的去做,上一件事情没有处理完,下一件事情是无法处理的
+ 异步 async:同时处理多件事情或者是上一件事情哪怕没有处理完,下一件事情也可以继续处理
进程:一个程序(或者浏览器打开一个页面)是开辟一个进程
线程:线程是进程中具体办事的,想同时办多件事,就需要开辟多个线程(一个进程中包含多个线程)
浏览器是多线程的,可以同时处理很多任务
- + GUI渲染线程:渲染HTML/CSS代码的,最后在页面中绘制出图形「GPU显卡」
- + JS引擎线程:渲染和解析JS的,会把栈内存中的代码进行解析和执行
- + 事件触发线程:监听事件是否触发的
- + 定时器触发线程:监听定时器是否到达时间的
- + 异步HTTP请求线程:用来从服务器获取资源文件或者数据的(link/script/img/ajax...)
- + WebWorker
但是JS是单线程的:因为浏览器只会分配一个线程(JS引擎线程)去渲染和解析JS,所以在JS中大部分代码都是同步的,如果此时主线程正在执行某些代码,那么其他事情都干不了
例如:循环就是同步代码,如果js中出现死循环那么主线程永远空闲不下来,其他事情都处理不了,整个页面就卡死了
JS的执行机制
JS中的代码一部分代码是异步的,并不是像想象中的同时执行很多代码,而是基于EventLoop浏览器的多线程机制"实现出监听排队等待的机制"
JS把我们的任务分为同步任务和异步任务
同步任务:都在主线程上执行形成了一个执行栈
异步任务又分为异步微任务和异步宏任务 异步任务添加到任务队列中(也被称为消息对列)
+ 异步微任务
+ requestAnimationFrame 「有争议」实现JS动画
+ Promise.then/catch/finally
+ async/await
+ queueMicrotask 可以创建一个异步的微任
+ IntersectionObserver 监听我们当前DOM元素与可视窗口交叉信息
+ MutationObserver 监听DOM元素改变的
+ process.nextTick「Node」执行下一个任务或队列
+ ...
+ 异步宏任务
+ 定时器 setTimeout/setInterval
+ 事件绑定/队列
+ XMLHttpRequest(ajax)/Fetch
+ MessageChannel 消息通知队列(很少用)
+ setImmediate「Node 的定时器」
+ ...
JS是单线程,实现异步的效果,要提供两个队列
WebAPI: 任务监听队列(监听任务是否执行)
EventQueue:任务/事件等待队列(能够执行的异步任务,都会在这个队列中,排队等待执行)
- 先执行执行栈中的同步任务
- 异步任务放到任务队列中
- 一旦执行栈中的所有同步任务执行完毕,系统会按次序读取任务队列中的异步任务,于是被读取的异步任务结束等待状态 进入执行栈开始执行
由于主线程不断的重复获得任务,执行任务 在获取任务,在执行所以这种机制被称为.事件循环机制(EventLoop)
[思考]
console.log(1);
document.onclick = function () {
console.log("click");
}
console.log(2);
setTimeout(function () {
console.log(3);
}, 3000)
[解析]
同步任务 console.log(1); console.log(1);
document.onclick = fn 提交给任务监听队列 看是否点击 如果点击了就放到任务队列里
setTimeout 也提交给任务监听队列,时间到了放到任务队列里
同步任务执行完去拿异步任务队列任务
先进先出 先拿微任务在拿宏任务
思考题2
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);
解析
答案: 2 4 5 7 9 3 1 6 8
总结
- settimeout:等待时间写0或者不写也不是立即执行,浏览器有最快的反应时间(谷歌5-7ms),设置0浏览器也是按照5-7毫秒来等待执行的--->保证定时器是异步编程
- 代码在栈中执行的时候,遇到异步任务代码,首先把其放置到webapi队列中监听,
- 在WebApi中浏览器会分配很多其他的线程,去监听任务是否可以执行了
- 当某个异步任务被监听到可以执行了也不会立即执行,而是先进入到EventQueue中排队等着
- 当同步代码执行完(主线程空闲下来)此时会每隔5-7ms在EventQueue中把正在排队等待执行的异步任务,拿到栈中,交给主线程去执行
- 设定个定时器 即使到达等待时间,也不一定会执行对应的函数(因为此时主线程可能还没忙完)
- 如果有可执行的异步微任务,不论是否比宏任务晚到的可执行,都要把先把微任务执行,微任务都处理完才去处理异步宏任务
- 如果异步类型一样,谁先到达可执行的(先到达的在对列最开始排着),先把谁处理
定时器
语法规范 window.setTimeout(调用函数,延迟时间)
- window调用时可以省略
- 延时时间是毫秒,但是可以省略,如果省略默认是0
- 这个调用函数可以直接写函数,还可以写函数名 还有一个写法'函数名;()'
- 页面中有多个定时器时我们经常给定时加标识符(名字)
创建定时器的四步
1. 声明一个全局的对象来接受定时器
2.启动定时器
3.停止定时器
4定时器名=null
三种定时器
周期性定时器 以一定的时间隔执行代码 setInterval
一次性定时器 在一个设定的时间间隔之后来执行代码而不是函数被调用后立即执行 setTimeout
requestAnimationFrame 最大的优势是由系统来决定回调函数的执行时机。具体一点讲,如果屏幕刷新率是60Hz,那么回调函数就每16.7ms被执行一次,如果刷新率是75Hz,那么这个时间间隔就变成了1000/75=13.3ms,换句话说就是,「requestAnimationFrame的步伐跟着系统的刷新步伐走」,「它能保证回调函数在屏幕每一次的刷新间隔中只被执行一次」,「这样就不会引起丢帧现象,也不会导致动画出现卡顿的问题」。
清除定时器
clearInterval(timer);
clearTimeout(timer);
----------------------------------------------------完结,散花--------------------------------------------------
---------------------接受大佬们的批改,欢迎评论留言---------------------------------------------------------