A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 专注的一批 中级黑马   /  2019-12-26 14:30  /  1185 人查看  /  0 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文


在一些特殊的场景中我们可能会需要使用计时器,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方法和windowsetTimeout做了个简单的比较,两者从创建到执行所用时间相差不大,经过多次测试windowsetTimeout也没有我想象中那么准确。
总结:游戏项目中避免使用windowsetTimeoutsetInterval方法,若有需要应使用requestAnimationFrame去实现相关方法,将回调控制在了每帧的刷新中。
ps:类似的H5引擎,egret也有自己实现setTimeout方法,想必一方面也是因为浏览器setTimeout存在不确定的因素吧。

0 个回复

您需要登录后才可以回帖 登录 | 加入黑马