UNPKG

mqrpc

Version:

💫 Easy RPC over RabbitMQ

129 lines (128 loc) • 4.57 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const promises_1 = require("./promises"); class TimeoutExpired extends Error { constructor(timeout) { super(`${timeout.id} expired after ${timeout.length}ms`); this.timeout = timeout; } } exports.TimeoutExpired = TimeoutExpired; /** * @private * * Activates a new timeout and returns it. The given reject will be called * with a TimeoutExpired error if the timer expires. * * @param {Timeout} timeout The timeout to activate * @param {Function} reject Called with a TimeoutExpired error when the timer expires */ const activateTimeout = (timeout, reject) => { return Object.assign({}, timeout, { handle: setTimeout(() => { reject(new TimeoutExpired(timeout)); }, timeout.length) }); }; class Timer { constructor() { this.entries = new Map(); } /** * Adds and starts the given timeouts for the given `entryId`. If an entry * with the same ID exists, timeouts will be appended to it. Otherwise, a new * entry is created. * * The promise returned will never resolve, but it will reject when one of the * timeouts expires. * * @param {string} entryId An ID under which to add the timeouts. * @param {Timeout[]} ...timeouts Timeout objects, with an ID and length in ms. * @return {Promise<void>} Rejects when a timeout expires. Never resolves. */ addTimeouts(entryId, ...timeouts) { const entry = this.ensureEntry(entryId); timeouts.forEach(timeout => { if (entry.timeouts.has(timeout.id)) { throw new Error(`Timeout with ID ${timeout.id} already exists for entry ${entryId}`); } entry.timeouts.set(timeout.id, activateTimeout(timeout, entry.reject)); }); return entry.promise; } /** * Restarts all timeouts with the given IDs for the given entry. * * @param {string} entryId The ID under which the timeouts are. * @param {string[]} ...timeoutIds The IDs for timeouts to restart. */ restartTimeouts(entryId, ...timeoutIds) { const entry = this.entries.get(entryId); if (!entry) throw new Error(`Entry with ID ${entryId} does not exist`); timeoutIds.forEach(id => { const timeout = entry.timeouts.get(id); if (!timeout) throw new Error(`Timeout with ID ${id} does not exist for entry ${entryId}`); clearTimeout(timeout.handle); entry.timeouts.set(id, activateTimeout(timeout, entry.reject)); }); } /** * Stops and removes the timeouts with the given IDs from the given entry. * * @param {string} entryId The ID under which the timeouts are. * @param {string[]} ...timeoutIds The IDs for timeouts to stop & remove. */ removeTimeouts(entryId, ...timeoutIds) { const entry = this.entries.get(entryId); if (!entry) throw new Error(`Entry with ID ${entryId} does not exist`); timeoutIds.forEach(id => { const timeout = entry.timeouts.get(id); if (!timeout) return; // idempotent clearTimeout(timeout.handle); entry.timeouts.delete(id); }); } /** * Removes an entry. This stops and removes all its timeouts as well. * * @param {string} entryId The ID for the entry to remove. */ remove(entryId) { const entry = this.entries.get(entryId); if (!entry) return; // idempotent entry.timeouts.forEach(timeout => clearTimeout(timeout.handle)); entry.timeouts.clear(); this.entries.delete(entryId); } /** * Removes every entry and every timeout. */ clear() { for (const entryId of this.entries.keys()) { this.remove(entryId); } } /** * @protected * * Returns an existing Entry or creates a new one. * * @param {string} entryId The Entry's ID to create or fetch. * @return {Entry} A new or existing Entry */ ensureEntry(entryId) { let entry = this.entries.get(entryId); if (entry) return entry; const [promise, { reject }] = promises_1.newPromiseAndCallbacks(); entry = { promise, reject, timeouts: new Map() }; this.entries.set(entryId, entry); return entry; } } exports.default = Timer;