不管 setTimeout 或 setInterval 是如何实现的,这里有一个非常有效的想法。如果您在未来 N 个不同时间安排了 N 个事件,请创建一个对象数组,其中每个对象都有一个事件到期时间的属性和一个告诉您它是什么类型的事件的属性(回调或一些其他标识符)。最初按 time 属性对该数组进行排序,以便下一次在事件的前面,最远的时间在最后。
然后,查看数组的前面,计算直到该事件的时间并setTimeout()
在该持续时间内执行。当setTimeout()
火灾发生时,查看数组的开头并处理所有到达时间的事件。如果在处理一个事件之后,您需要安排它的下一次发生,请计算未来应该触发的时间,并从开始到结束遍历数组,直到找到它之后的事件并将其插入到该事件之前(以保持数组排序)。如果没有找到,将其插入到最后。在处理了到达时间的数组前面的所有事件后,计算数组前面的事件的增量时间,并setTimeout()
为该间隔发出一个新的。
这是一些伪代码:
function orderedQueue() {
this.list = [];
}
orderedQueue.prototype = {
add: function(time, callback) {
var item = {}, added = false;
item.time = time;
item.cb = callback;
for (var i = this.list.length - 1; i >= 0; i--) {
if (time > this.list[i].time) {
// insert after the i item
this.list.splice(i + 1, 0, item);
added = true;
break;
}
}
// if no item was after this item,
// then put this on the front of the array
if (!added) {
this.list.unshift(item);
}
},
addDelta(delta, callback) {
var now = new Date().getTime();
this.add(now + delta, callback);
},
waitNext: function() {
// assumes this.list is properly sorted by time
var now = new Date().getTime();
var self = this;
if (this.list.length > 0) {
// set a timer for the first item in the list
setTimeout(function() {
self.process();
}, this.list[0].time - now);
}
},
process: function() {
var now,item;
// call all callbacks who's time has been reached
while (this.list.length) {
now = new Date().getTime();
if (this.list[0].time <= now) {
// remove front item from the list
item = this.list.shift();
// call the callback and pass it the queue
item.cb(this);
} else {
break;
}
}
// schedule the next item
this.waitNext();
}
}
而且,这通常是您将如何使用它:
var q = new orderedQueue();
// put initial events in the queue
q.addDelta(100, f1);
q.addDelta(1000, f2);
q.addDelta(5000, f3);
q.addDelta(10000, f4);
q.addDelta(200, f5);
q.addDelta(100, f1);
q.addDelta(500, f1);
// start processing of queue events
q.waitNext();