Асинхронная ф-я может вызываться из разных мест большого кода, с передачей коллбэка для возврата результата.
Есть ограничение: нельзя вызывать эту ф-ю чаще 3 раз в секунду.
Не могу сообразить, как проще сделать очередь задач.
Сделал два буфера: массив времён последних трёх вызовов times и массив объектов заданий tasks, где каждый содержит параметры вызова и callback.
Перед выполнением очередного вызова необходимая задержка определяется как неотрицательная разность текущего времени c моментом times[0]. После выполнения вызова, текущее время заносится в хвост: times.push(ts); times = times.slice(-3)
Ф-я вызывается с необх. таймаутом либо по поступлении первого объекта в пустую очередь, либо по завершении очередного задания из очереди.
Есть ли в каком-нибудь JS фреймворке похожий функционал, чтобы списать, и правильно ли я подхожу к задаче перестроения асинхронных вызовов в последовательность?
App.timeSpan = 1000;
App.times = [];
App.tasks = [];
// точка входа
App.api = function( method, params, callback) {
var toWait, dt, ts = (new Date()).getTime();
this.tasks.push({
method: method,
params: params,
callback: callback
});
if( this.tasks.length == 1) {
if( this.times.length > 2) {
dt = ts - this.times[0];
toWait = ( dt < this.timeSpan) ? this.timeSpan - dt : 0;
} else {
toWait = 0;
}
window.setTimeout(this.execute.bind(this), toWait);
}
}
App.execute = function(){
if( this.tasks.length == 0) return;
// вызов ТОЙ ф-ии
VK.api( this.tasks[0].method, this.tasks[0].params, this.executed.bind(this));
}
App.executed = function(r){
var ts = (new Date()).getTime() // timestamp in seconds
,dt
,toWait
;
this.times.push( ts);
this.times = this.times.slice(-3);
if( this.times.length > 2) {
dt = ts - this.times[0];
toWait = ( dt < this.timeSpan) ? this.timeSpan - dt : 0;
} else {
toWait = 0;
}
if( this.tasks.length > 1) {
window.setTimeout(this.execute.bind(this), toWait);
}
this.tasks.shift().callback.call(this, r);
}
Ответ
По-моему, радикально упростить код не получится. Получилось лишь сделать его чуть более "красивым":
//for tests
function mock(m, p, callback) {
var time = Math.floor(Math.random() * 300);
setTimeout(function() {
callback(m + ' ' + p);
}, time);
}
//for tests
function callbackMock(r) {
console.log("Callbacked: " + r);
}
function App() { }
App.callsPerTimeSpan = 3;
App.timeSpan = 1000;
App.times = [];
App.tasks = [];
App.handling = false;
App.taskHandler = mock; //mock - for tests
App.api = function(method, params, callback) {
this.tasks.push({
method: method,
params: params,
callback: callback
});
if (!this.handling) {
this.handling = true;
this.execute();
}
};
App.execute = function() {
if (this.tasks.length == 0) {
this.handling = false;
return;
}
var toWait = 0;
if (this.times.length == this.callsPerTimeSpan) {
var ts = this.getCurrentTime();
var dt = ts - this.times[0];
toWait = (dt < this.timeSpan) ? this.timeSpan - dt : 0;
}
var task = this.tasks[0];
var that = this;
window.setTimeout(function() {
that.taskHandler(task.method, task.params, that.executed.bind(that));
}, toWait);
};
App.executed = function(r) {
var ts = this.getCurrentTime();
this.times.push(ts);
this.times = this.times.slice(-this.callsPerTimeSpan);
var task = this.tasks.shift();
var that = this;
setTimeout(function() {
task.callback.call(that, r);
}, 0); //async call
this.execute();
};
App.getCurrentTime = function() {
return (new Date()).getTime();
};
//for tests
for (var i = 0; i < 10; i++) {
App.api(String.fromCharCode(i + 65), i, callbackMock);
}
"Красота" в первую очередь включает в себя отсутствующий дублированный код расчета времени и исполнения следующей задачи.
Пример с выводом отладочной информации в fiddle
Комментариев нет:
Отправить комментарий