From 3da6befe1058816f122e269f7e37bde9a83c970b Mon Sep 17 00:00:00 2001 From: Book Pauk Date: Sun, 26 Jan 2020 18:38:09 +0700 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=20=D0=BA=D0=BB=D0=B0=D1=81=D1=81=20LimitedQueue=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20=D0=BE=D1=80=D0=B3=D0=B0=D0=BD=D0=B8=D0=B7=D0=B0?= =?UTF-8?q?=D1=86=D0=B8=D0=B8=20=D0=BE=D1=87=D0=B5=D1=80=D0=B5=D0=B4=D0=B5?= =?UTF-8?q?=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/core/LimitedQueue.js | 120 ++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 server/core/LimitedQueue.js diff --git a/server/core/LimitedQueue.js b/server/core/LimitedQueue.js new file mode 100644 index 00000000..fd9d68e0 --- /dev/null +++ b/server/core/LimitedQueue.js @@ -0,0 +1,120 @@ +const cleanPeriod = 60*1000;//1 минута +const cleanTimeout = 60;//timeout в минутах (cleanPeriod) + +class LimitedQueue { + constructor(enqueueAfter = 10, size = 100, timeout = cleanTimeout) {//timeout в минутах (cleanPeriod) + this.size = size; + this.timeout = timeout; + + this.freed = enqueueAfter; + this.listeners = []; + + this.timer = setTimeout(() => { this.periodicClean(); }, cleanPeriod); + } + + _addListener(listener) { + this.listeners.push(Object.assign({regTime: Date.now()}, listener)); + } + + //отсылаем сообщение первому ожидающему и удаляем его из списка + _emitFree() { + if (this.listeners.length > 0) { + let listener = this.listeners.shift(); + listener.onFree(); + + const now = Date.now(); + for (let i = 0; i < this.listeners.length; i++) { + listener = this.listeners[i]; + listener.regTime = now; + listener.onPlaceChange(i + 1); + } + + } + } + + get(onPlaceChange) { + return new Promise((resolve, reject) => { + if (this.destroyed) + reject('destroyed'); + + const take = () => { + if (this.freed <= 0) + throw new Error('Ошибка получения ресурсов в очереди ожидания'); + + this.freed--; + + let returned = false; + return { + ret: () => { + if (!returned) { + this.freed++; + this._emitFree(); + returned = true; + } + } + }; + }; + + if (this.freed > 0) { + resolve(take()); + } else { + if (this.listeners.length < this.size) { + this._addListener({ + onFree: () => { + resolve(take()); + }, + onError: (err) => { + reject(err); + }, + onPlaceChange: (i) => { + if (onPlaceChange) + onPlaceChange(i); + } + }); + if (onPlaceChange) + onPlaceChange(this.listeners.length); + } else { + reject('Превышен размер очереди ожидания'); + } + } + }); + } + + destroy() { + if (this.timer) { + clearTimeout(this.timer); + this.timer = null; + } + + for (const listener of this.listeners) { + listener.onError('destroy'); + } + this.listeners = []; + + this.destroyed = true; + } + + periodicClean() { + try { + this.timer = null; + + const now = Date.now(); + //чистка listeners, убираем зависшие в очереди на одном месте + let newListeners = []; + for (const listener of this.listeners) { + if (now - listener.regTime < this.timeout*cleanPeriod - 50) { + newListeners.push(listener); + } else { + listener.onError('Время ожидания в очереди истекло'); + } + } + this.listeners = newListeners; + } finally { + if (!this.destroyed) { + this.timer = setTimeout(() => { this.periodicClean(); }, cleanPeriod); + } + } + } +} + +module.exports = LimitedQueue; \ No newline at end of file