定时器与异步
定时器
控制在某个固定的时刻往进程中添加代码执行。
setTimeout
执行之后返回一个正整数,是定时器的编号,可以用这个编号清除定时器,不同的定时器编号会累加
- 在某段时间之后执行代码
setTimeout
,
第一个参数是需要执行的代码或者回调函数,必须,
第二个参数是延时,不写默认为 0,单位毫秒(1s = 1000ms)
第三个以后是需要执行的代码传入的参数
let foo = (x, y) => console.log(x + y)
setTimeout(foo, 1000, 2, 3) //返回一个正整数定时器编号
//1s之后打印出5
因为
window.setTimeout === setTimeout //true
这里不管在哪调用 setTimeout 的主体是 window
let o = {
a: 1,
}
function foo() {
setTimeout(function() {
console.log(this)
}, 1000)
}
foo.call(o) //这里指向window
因为是window
调用的setTimeout
所以不可以修改。定时器永远是浏览器调用
解决方法:
let o = {
a: 1,
}
function foo() {
setTimeout(
function() {
console.log(this)
}.bind(o),
1000
) //强行修改回调函数的this,返回的函数和原函数已经不是同一个了
}
foo() //这里指向o
或者:
let o = {
a: 1,
}
function foo() {
setTimeout(() => {
//硬绑定到foo上下文的this中
console.log(this)
}, 1000)
}
foo.call(o) //这里指向o
清除定时器:clearInterval
传入一个参数,就是定时器的序号。页面唯一。
let timer = setTimeout(function() {}, 5000)
clearInterval(timer) //上面那个定时器就删掉了。
运行代码后每隔一段时间执行相应代码:
setInterval
,和setTimeout
有相似性质第一个参数是需要执行的代码或者回调函数,必须,
第二个参数是延时,不写默认为 0,单位毫秒(1s = 1000ms)
第三个以后是需要执行的代码传入的参数
let timer = setInterval(
function(a, b) {
console.log(a + b)
},
1000,
2,
3
) //每隔1000ms打印依次5
clearInterval(timer) //通过定时器序号清除定时器
同步与异步
浏览器的 js 引擎,掌控了所有的 JS 代码运行。
JS 是单线程运行的,换句话说,同一时刻只能做一件事情。
for (let i = 0; i < 900000000; i++) {} //页面卡死一小会,无法操作
虽然 JS 执行和浏览器渲染虽然不是同一个线程,但是 JS 涉及页面元素操作,修改操作完成之前渲染会被锁死,JS 完成之后才会渲染。这就是单线程的性质。
同步
当读取到 JS 代码的时候:代码会从上往下执行,当前面的代码执行完成返回之前,后面的代码是不会执行的。其余操作也是不会执行的。
console.log(1)
console.log(2)
for (let i = 0; i < 900000000; i++) {} //页面卡死一小会,无法操作
console.log(3) //在上行代码执行完成之前是不会打印的
//1 2 等一会儿 3
异步
异步的含义是相对于同步的,某行代码的执行单独开辟线程处理,与主线程无关,返回结果传递到主线程
但是 JS 运行是单线程的,所以不存在真正的异步
JS 执行的时候会依照一个顺序执行:
先执行完同步队列----->再执行异步队列
浏览器执行同步队列里的代码,
当遇到 setTimeout 或者 setInterval 或者点击事件等,会在必要的时刻将代码标识扔到异步队列中,异步代码会返回结果表示代码执行完成,实际代码没有执行。
同步执行完成会处于空闲状态。
处于空闲状态(执行间隙)才会不停扫描异步队列,将扫描到的标识为需要执行的异步代码执行
回调
浏览器 dom 事件:响应用户点击,图片加载完成,vue 组件的生命周期钩子 也是属于异步
节流和防抖
防抖 debounce
在事件被触发 n 秒后再执行回调函数,如果在这 n 秒内又被触发,则重新计时
节流 throttle
规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。
推荐使用Lodash函数库来使用节流和防抖
请求动画帧
requestAnimationFrame(foo(time))
电脑不卡的时候相当于setTimeout(foo,1000/60)
回调函数的参数 time 可以获取打开浏览器到现在经历的毫秒数
运动架
CSS3 当中可以通过 animation 做动画。JS 中的动画依赖运动框架。
已知起始位置和目标位置:传入运动时间,通过定时器设置完成动画效果。
需求:一个函数传入对象,时间,变换值,回调(动画执行完成后执行的操作)
function getStyle(ele) {
return ele.currentStyle || getComputedStyle(ele)
}
//简单的解决兼容问题
window.requestAnimationFrame =
window.requestAnimationFrame ||
function(cb) {
return setTimeout(cb, 1000 / 60)
}
window.cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout
function animation(ele, data = {}, time = 500, cb) {
/*
* ele => 已获取的HTML标签对象
* data => 需要改变的样式:目标数值 可以带单位
* time => 改变样式所需要的总时间 单位是毫秒
* 例如 : animation(box,{width:'1000px',height:'1000px'},5000)
*/
var startValue = {}
var changeValue = {}
var startTime = new Date()
var eleStart = getStyle(ele)
for (var key in data) {
startValue[key] = isNaN(Number.parseFloat(eleStart[key])) ?
0 :
Number.parseFloat(eleStart[key])
changeValue[key] = Number.parseFloat(data[key]) - startValue[key]
}
run()
function run() {
var nowTime = new Date() - startTime
var t1 = nowTime / time
for (var key in changeValue) {
var val = t1 * changeValue[key] + startValue[key]
ele.style[key] = val + "px"
}
if (t1 >= 1) {
cancelAnimationFrame(run)
cb && cb()
} else {
requestAnimationFrame(run)
}
}
}
运动框架核心思想:
已用时间/总时间=已走路程/总路程
已知总时间和总路程还有帧数