在canvas动画的文章中用到了requestAnimationFrame,丢弃了传统中使用的setInterval
setInterval
以固定的时间间隔,重复运行一段代码.
requestAnimationFrame
setInterval()的现代版本;在浏览器下一次重新绘制显示之前执行指定的代码块,从而允许动画在适当的帧率下运行,而不管它在什么环境中运行. 那么两者的相同之处呢?都是作为Javascript单线程中异步而存在的。
Javascript单线程
进程和线程
进程就好比工厂(GPU)的车间,而车间的工人就是线程。每个车间独立运作,工人协同合作完成工作。
1.一个进程包含多个线程,进程的内存空间是线程共享的
2.进程是相互独立的
3.进程是操作系统分配的最小单位,线程是程序运行最小单位
浏览器中线程
定时器线程
浏览器定时计数器是一个单独的线程,触发定期行为后进入事件触发线程。
假设是在javascript线程,会因为阻塞导致计数不准确。
事件触发线程
当一个事件(鼠标点击、ajax请求、定时器等)触发后,会添加到队列中,等待计js线程处理。
异步http请求线程
浏览器有一个单独的线程用于处理ajax请求,当有回调时,进入事件触发线程
javascript线程
负责处理解析和执行javascript脚本程序
GUI 渲染线程
选择浏览器中html,以及对应的重绘。在javascript线程运行脚本期间,GUI是被暂时挂起的
Event Loop事件循环机制
我最初的理解是:
同步的任务在js线程上执行,形成一个执行栈;setTimeout、setInterval和requestAnimationFrame中的回调作为js中的异步任务,通过触发对应的线程,将回调放入事件队列中;执行栈中的内容执行完成后,GPU会进行重绘,然后js引擎会通过系统去读取事件队列中的内容,将内容放在执行栈中执行。
在实际的过程中呢?
在事件循环的过程中,异步事件返回的结果会被放在事件队列中。根据异步事件的类型,异步事件会被分为宏任务和微任务队列中。执行栈先去执行宏任务,当宏任务中内容为空,主线程会查看当前的微任务是否有事件存在,如果不存在会去执行下一次宏任务,如果存在,先执行当前的微任务。
异步任务解释,
event-loop。
浏览器中的宏任务主要包含定时器、io操作,微任务主要是promise、async,requestAnimationFrame姑且作为一种特殊的任务,在执行完微任务后触发。MDN中讲到:告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。
首先一个简单的例子:
document.body.style = 'background:blue'
console.log(1)
new Promise(res=>{
document.body.style = 'background:gray'
console.log(2)
res(2)
}).then((r)=>{
console.log(r)
document.body.style = 'background:black'
})
console.log(3)
setTimeout(() => {
console.log(4)
document.body.style = 'background:pink'
}, 10)
控制台最终效果1、2、3、2、4。
1.执行宏任务中的内容颜色变蓝
2.打印1
3.颜色变灰
4.打印2
5.打印3
6.执行微任务中的打印promise返回结果2
7.颜色变黑
8.GPU渲染
9.执行新的宏任务
10.打印4
11.颜色变粉
浏览器的样式为先黑色在变成粉色,会忽略掉第三个步骤,因为这个时候微任务还没有执行完,所以重绘的时候颜色会直接变成黑色
接下来看一个相对复杂的例子
console.log(1)
setTimeout(() => {
console.log('setTimeout')
}, 10);
new Promise((res,rej)=>{
res(11)
console.log("Promise")
}).then(res=>{
console.log(res+"PromiseThen")
}).catch(rej=>{
})
window.requestAnimationFrame(()=>{
console.log('requestAnimationFrame')
})
console.log("hah")
let aa = new Promise((res,rej)=>{
res(2)
})
async function bb() {
let a = await aa;
try {
console.log(a+"try")
} catch (error) {
console.log(a+"catch")
}
}
bb()
console.log("end")
所以打印结果如下:
1----Promise----hah----end----11PromiseThen----2try----requestAnimationFrame----setTimeout
这里的执行顺序:
1.宏任务开始 打印1
2.触发定时器线程、将回调放入事件队列中
3.promise打印promise,将promise的回调放入当前的微任务中
4.打印hah
5.继续执行promise,将回调放入微任务中
6.打印end
7.执行当前的微任务,打印console.log(res+"PromiseThen")
打印11PromiseThen
8.执行当前的微任务,打印console.log(a+"try")
打印果2try
9.执行requestAnimationFrameconsole.log('requestAnimationFrame')
打印requestAnimationFrame
10.GUP渲染
11.执行下次宏任务定时器中的console.log('setTimeout')
打印setTimeout
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!