在一些特殊的场景中我们可能会需要使用计时器,n秒后执行一段逻辑,或者循环执行一段逻辑。 这个时候我们可能首先想到的会是setInterval()或者setTimeout()两个方法。但是使用这两个方法可能会为我们的工程带来隐患。 记录如下代码片段: setTimeout(()=>{ console.log("1") },0) console.log("2") //打印结果 //2 //1 可能会有部分人和我会有同样的疑惑,为什么会先打印2。若有此疑问的同学还需要学习一下js的任务队列和调用栈。 setTimeout传入方法在什么时候执行会变得难以捉摸。所以为解决setTimeout所带来的隐患,需要自己实现相同功能的方法。 这里不考虑浏览器兼容的问题,用requestAnimationFrame来解决setTimeout的问题。 class Timer{ constructor(){ this.lastTime = 0; //最后一次更新的时间 this.timeIndex = 0; this.timeOutArr = []; //setTimeout 实例的数组 } update(timestamp){ if(this.lastTime == 0){ this.lastTime = timestamp; } let interval = timestamp - this.lastTime; //上一帧到当前帧的时间间隔 this.lastTime = timestamp; //更新时间 this.refreshTime(interval); //刷新setTimeout 的时间 } refreshTime(interval){ let length = this.timeOutArr.length; for(let i = length - 1;i>=0;i--){ let timeOut = this.timeOutArr; timeOut.time -= interval; //timeOut实例的剩余时间 if(timeOut.time <= 16){ //timeOut剩余时间不足1帧 可以立即执行 timeOut.func.call(timeOut.thisObj) //调用回调方法 this.timeOutArr.splice(i,1) //移出回调方法 } } } /** * time秒后回调 * @param fun 要执行的回调方法 * @param thisObj 执行方法的作用域 避免出错 * @param time 在time秒后执行回调 */ setTimeout(fun,thisObj,time){ let obj = { func:fun, thisObj:thisObj, time:time } this.timeOutArr.push(obj); this.timeIndex++; } } let timer = new Timer(); function update(timestamp){ timer.update(timestamp); requestAnimationFrame(update) } requestAnimationFrame(update) function test(){ function(){ //外汇技术指标 http://www.kaifx.cn/mt4/kaifx/1791.html console.time('timer timeout用时') timer.setTimeout(()=>{ console.timeEnd('timer timeout用时') //获取setTimeout从创建到执行的时间 },this,2000) console.time('window timeout用时') setTimeout(() => { console.timeEnd('window timeout用时') }, 2000); //打印结果 //windows timeout用时: 2001.82373046875ms //timer timeout用时: 2004.0400390625ms } test();这里简单做了一个timer类,并实现了setTimeout的方法,每一帧都去刷新timer,并记录最后的刷新时间。 对实现的setTimeout方法和window的setTimeout做了个简单的比较,两者从创建到执行所用时间相差不大,经过多次测试window的setTimeout也没有我想象中那么准确。 总结:游戏项目中避免使用window的setTimeout和setInterval方法,若有需要应使用requestAnimationFrame去实现相关方法,将回调控制在了每帧的刷新中。 ps:类似的H5引擎,egret也有自己实现setTimeout方法,想必一方面也是因为浏览器setTimeout存在不确定的因素吧。
|