Meteor 1.6:清除由另一个 Meteor 方法调用启动的服务器上的超时

Meteor 1.6: clearing timeout on server started by another meteor method call

我正在使用 Meteor 1.6.0.1 用例是客户端每 5 秒调用一次流星方法来告诉服务器它在线并且服务器存储最后一个事件时间,在线集合。

Meteor.methods({
    'ping.online'() {
        MyColl.update({_id:this.userId}, {$set:{last_online:Date.now(), online:true}})
    }
});

如果 1 分钟内没有事件,我也想将其标记为离线。为此,我考虑了以下方法。

Meteor.methods({
    'ping.online'() {
        MyColl.update({_id:this.userId}, {$set:{last_online:Date.now(), online:true}});
        let timer = Meteor.setTimeout(()=>{
           MyColl.update({_id:this.userId}, {$set:{online:false}});
        }, 60*1000)
    }
});

为了使它起作用,我必须在 5 秒后发出下一个 ping 时调用 Meteor.clearTimeout(timer)。我很困惑我应该如何存储计时器值并在来自同一 client/user.

的下一次调用中使用它

我本可以只存储 last_online 时间并使用相同的逻辑将其离线显示,但是当我将其发布到客户端时,客户端会收到太多更新,因为每个字段都会频繁更改用户。这导致 ui 由于数据更改而在一秒钟内多次更新并显示闪烁。

一般注意事项:那里有 Meteor 的软件包,可以自动且非常有效地解决用户在线状态。仍然发布以下代码以支持对使用第二个“支持”集合跟踪特定字段的方法的一般理解。


编辑:在我发现 Meteor.setTimeout return 是句柄而不是 id 之后,我不得不重写我的解决方案。更新版本在最下方


一种可能的方法是创建第二个名为 Timers 且具有 key/value 架构的集合:

{
  timerId:String,
  userId:String,
}

通过这样做,您可以将当前计时器存储在该集合的文档中。如果 timerDoc 存在于方法 运行 上,您可以通过存储的计时器 id 清除计时器:

Meteor.methods({
    'ping.online'() {
        // get timer, if present
        // and clear timeout
        const timerDoc = Timers.findOne({userId:this.userId})
        if (timerDoc && timerDoc.timerId) {
            Meteor.clearTimeout(timerDoc.timerId);
        }

        // update collection
        MyColl.update({_id:this.userId}, {$set:{last_online:Date.now(), online:true}});

        // create new timer
        let timer = Meteor.setTimeout(()=>{
           MyColl.update({_id:this.userId}, {$set:{online:false}});
           Timers.remove({_id: timerDoc._id});
        }, 60*1000);

        // update Timers collection
        if (timerDoc) {
            Timers.update({_id: timerDoc._id}, {$set: {timerId: timer} });
        }else{
            Timers.insert({
                timerId: timer,
                userId: this.userId,
            });
        }
    }
});

编辑

上面的代码不适用于 Meteor.setTimeout,因为它不是 return 计时器 ID,而是句柄 as described in the docs。 您可以使用充当字典的普通对象来缓存计时器对象。

但是,这不会阻止您缓存计时器句柄并以与上述代码类似的方式使用它。

// your timers dictionary
const Timers = {};

Meteor.methods({
    'ping.online'() {
        // get timer, if present
        // and clear timeout
        const existingTimer = Timers[this.userId];
        if (existingTimer) {
            Meteor.clearTimeout(existingTimer.timerId);
            delete Timers[this.userId];
        }

        // update collection
        MyColl.update({_id:this.userId}, {$set:{last_online:Date.now(), online:true}});

        // create new timer
        let timerHandle = Meteor.setTimeout(()=>{
           MyColl.update({_id:this.userId}, {$set:{online:false}});
           delete Timers[this.userId];
        }, 60*1000);

        // store timerHandle in dictionary
        Timers[this.userId] = timerHandle;
    }
});

这种方法不是持久的,但也不是必须的,因为信息是在服务器启动后需要的,并且所有计时器都已重置。尽管如此,请记住另一个示例(使用集合),因为它是一种经常出现的模式,当您需要使支持信息持久化时。