#javascript #алгоритм #async_programming
Асинхронная ф-я может вызываться из разных мест большого кода, с передачей коллбэка для возврата результата. Есть ограничение: нельзя вызывать эту ф-ю чаще 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); }
Ответы
Ответ 1
По-моему, радикально упростить код не получится. Получилось лишь сделать его чуть более "красивым": //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.Ответ 2
Есть ли в каком-нибудь JS фреймворке похожий функционал Есть. В jQuery точно есть throttle и debounce, в других тоже что-то похожее. Вообще, гуглится замечательно. Да и реализуется несложно. Например, вот расширенная версия с комментариями. там «проглатываются» лишние вызовы функции. В моей задаче этого нельзя допускать Тогда так: function queue(f, ms) { var calls = null; return function () { if (calls == null) { calls = []; setTimeout(function go() { // Гарантируем асинхронный вызов if (calls.length) { f.apply(null, calls.shift()) setTimeout(go, ms); } else { calls = null; } }); } calls.push(arguments); } } Проверка: f = queue(function () { console.log(new Date, arguments) }, 1000) f(1); f(1, 2); f({}); setTimeout(f, 3500); setTimeout(f, 4500, [4, 5], "q"); setTimeout(f, 6000, 6); setTimeout(f, 8000, 8); setTimeout(f, 8500, 8.5); setTimeout(f, 9000, 9); setTimeout(f, 11000, 9);
Комментариев нет:
Отправить комментарий