Страницы

Поиск по вопросам

пятница, 26 октября 2018 г.

Как поставить вызовы асинхронной ф-ии в очередь, и не чаще 3 в секунду?

Асинхронная ф-я может вызываться из разных мест большого кода, с передачей коллбэка для возврата результата.
Есть ограничение: нельзя вызывать эту ф-ю чаще 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

Комментариев нет:

Отправить комментарий