一个SetInterval与多个SetTimeout

时间:2013-07-12 02:33:25

标签: javascript settimeout setinterval

我有一个名为Player的对象:

var Player = function(_id) {
    var time = new Date().getTime();

    this.act = function() {
        if (new Date().getTime()-time>=5000)
            this.destroy();
    }
}

当我创建这个玩家对象时,我想在5秒后destroy这个玩家。

现在我可以看到两种方法来解决这个问题:

  1. 如果我决定使用act方法(使用setInterval),它可能看起来像:

    Players = {};
    for(var i=0;i<5;i++)
        Players[i] = new Player(i);
    
    setInterval(
        function() {
            for(var i=0;i<5;i++)
                Players[i].act();
        },
        1
    );
    
  2. 如果我决定使用setTimeout,例如:

    var Player = function() {
        setTimeout(
            function() {
                this.destroy();
            },
            5000
        )
    }
    
  3. 使用方法#2意味着我不需要跟踪时间,也不需要特殊ID。

    然而,假设我有200名玩家,它会产生200个计时器,那不会效率低下吗?

    但是,如果我使用方法#1,它将一直运行,计时器将保持每毫秒运行,我觉得效率也很低。

    因此,就效率而言(包括精度也很好),哪种方法更好?

    解决方案

    我的最终代码如下:

    function Player(_id) {
        this.enqueue(_id);
    }
    
    Player.prototype = {
        queue: [],
        constructor: Player,
        enqueue: function (_id) {
            this.id = _id;
            var length = this.queue.push({
                expires: new Date().getTime() + 5000,
                instance: this
            });
    
            if (length === 1) this.dequeue();
        },
        dequeue: function () {
            var player = this.queue[0];
            var delay = player.expires - new Date().getTime();
            setTimeout(this.act.bind(player.instance,player.id), delay);
        },
        act: function () {
            console.log(this.id);
            this.queue.shift();
            if (this.queue.length)
                this.dequeue();
        }
    };
    

    如果我在chrome中进入开发人员的控制台并输入Player(4),然后等待2秒并输入Player(3),我会得到4,然后是两秒钟之后3。

    按预期工作,通过查看代码,我只使用一个setTimeout。

2 个答案:

答案 0 :(得分:3)

这两个世界的优点怎么样?您将按顺序创建Player个对象。因此,它们将以与创建时相同的顺序超时。记住这一点,你一次只需要一个setTimeout(对于Player对象,接下来会超时)。这是如何实现它:

function Player() {
    this.enqueue();
}

Player.prototype = {
    queue: [],
    constructor: Player,
    enqueue: function () {
        var length = this.queue.push({
            expires: new Date().getTime() + 5000,
            instance: this
        });

        if (length === 1) this.dequeue();
    },
    dequeue: function () {
        var player = this.queue[0];
        var delay = player.expires - new Date().getTime();
        setTimeout(this.act.bind(player.instance), delay);
    },
    act: function () {
        this.destroy();
        this.queue.shift();
        if (this.queue.length)
            this.dequeue();
    }
};

现在,每次创建Player的实例时,它都会将其添加到队列(enqueue)。如果队列中只有一个实例,则队列开始移动(dequeue)。超时后,实例被销毁并且队列移位。如果队列中仍有实例,则队列继续移动。

这样,只有一个setTimeout一次有效,下一个在第一个结束后开始。

答案 1 :(得分:2)

使用setTimer(或setInterval)不会为每个SE生成任何计时器,但会将事件放在已解析的事件队列中。

但是,是的,有很多这样的事件很明显,时间会变得越来越不准确,并使浏览器忙于尝试清空这些事件和其他事件(如绘制事件)的队列。

你可以做的是拥有一个包含自己状态的自我容器玩家对象。仅举例来说(考虑这个伪代码,因为我还没有测试过它):

function Player(name) {

    var status = 0,           //0=alive, 1=dying, 2=dead
        timeOfDeath,
        threshold = 5000;     //ms, time of dying

    /// get status of player
    this.getStatus = function() {return status}

    /// kill player
    this.kill = function() {
        if (status === 0) {
            status = 1;
            timeOfDeath = new Date().getTime();
        }
    }

    function animateDeath(progress) { /* something here */ }

    ///MAIN, update player. This will be driven by a
    ///common loop
    this.update = function() {

        switch(status) {
            case 0:
                /// do whatever the player do when alive
                break;
            case 1:
               ///player is dying, animate death

               ///diff determines progress of death
               var diff = new Date().getTime() - timeOfDeath;
               animateDeath(diff);
               if (diff > threshold)  status = 2;
               break;
        }
    }
}

现在我们有一个玩家对象,我们可以从一个公共循环更新:

var p1 = new Player('Bob');
var p2 = new Player('Lee');
...

function loop() {
    if (p1.getStatus < 2) p1.update();
    if (p2.getStatus < 2) p2.update();
    ...

    requestAnimationFrame(loop);
}

如果你现在通过调用p1.kill()来杀死一个玩家,这将由对象本身自动处理,并且当状态改变时,对象不会更新。

当然,这是一个非常基本的例子,只是展示了它的原理。

您需要考虑优化,例如将玩家对象放入数组,而不是迭代并从游戏中的其他组件中移除死亡玩家,如果可能的话使用原型(对象比数组慢,所以如果玩家信息可以放在一个简单的数组中,并使外部函数与对象中的函数相同,这将表现得更好......)等等。

不确定您是否可以同时开车,需要创建&#34;群组&#34;等等,但这种格式太宽泛了。

使用requestAnimationFrame(rAF)可以将更新同步到监视器的VBLANK,使动画更加流畅。由于它是一种更低级别的机制,它也能更好,更快地工作。