@grammyjs/runner
Version:
Scale grammY bots that use long polling
134 lines (133 loc) • 5.51 kB
TypeScript
/**
* A _decaying deque_ is a special kind of doubly linked list that serves as a
* queue for a special kind of nodes, called _drifts_.
*
* A decaying deque has a worker function that spawns a task for each element
* that is added to the queue. This task then gets wrapped into a drift. The
* drifts are then the actual elements (aka. links) in the queue.
*
* In addition, the decaying deque runs a timer that purges old elements from
* the queue. This period of time is determined by the `taskTimeout`.
*
* When a task completes or exceeds its timeout, the corresponding drift is
* removed from the queue. As a result, only drifts with pending tasks are
* contained in the queue at all times.
*
* When a tasks completes with failure (`reject`s or exceeds the timeout), the
* respective handler (`catchError` or `catchTimeout`) is called.
*
* The decaying deque has its name from the observation that new elements are
* appended to the tail, and the old elements are removed at arbitrary positions
* in the queue whenever a task completes, hence, the queue seems to _decay_.
*/
export declare class DecayingDeque<Y, R = unknown> {
private readonly taskTimeout;
private readonly worker;
private readonly catchError;
private readonly catchTimeout;
/**
* Number of drifts in the queue. Equivalent to the number of currently
* pending tasks.
*/
private len;
/** Head element (oldest), `null` iff the queue is empty */
private head;
/** Tail element (newest), `null` iff the queue is empty */
private tail;
/**
* Number of currently pending tasks that we strive for (`add` calls will
* resolve only after the number of pending tasks falls below this value.
*
* In the context of `grammy`, it is possible to `await` calls to `add` to
* determine when to fetch more updates.
*/
readonly concurrency: number;
/**
* Timer that waits for the head element to time out, will be rescheduled
* whenever the head element changes. It is `undefined` iff the queue is
* empty.
*/
private timer;
/**
* List of subscribers that wait for the queue to have capacity again. All
* functions in this array will be called as soon as new capacity is
* available, i.e. the number of pending tasks falls below `concurrency`.
*/
private subscribers;
private emptySubscribers;
/**
* Creates a new decaying queue with the given parameters.
*
* @param taskTimeout Max period of time for a task
* @param worker Task generator
* @param concurrency `add` will return only after the number of pending tasks fell below `concurrency`. `false` means `1`, `true` means `Infinity`, numbers below `1` mean `1`
* @param catchError Error handler, receives the error and the source element
* @param catchTimeout Timeout handler, receives the source element and the promise of the task
*/
constructor(taskTimeout: number, worker: (t: Y) => Promise<void>, concurrency: boolean | number, catchError: (err: R, elem: Y) => void | Promise<void>, catchTimeout: (t: Y, task: Promise<void>) => void);
/**
* Adds the provided elements to the queue and starts tasks for all of them
* immediately. Returns a `Promise` that resolves with `concurrency - length`
* once this value becomes positive.
* @param elems Elements to be added
* @returns `this.capacity()`
*/
add(elems: Y[]): Promise<number>;
empty(): Promise<void>;
/**
* Returns a `Promise` that resolves with `concurrency - length` once this
* value becomes positive. Use `await queue.capacity()` to wait until the
* queue has free space again.
*
* @returns `concurrency - length` once positive
*/
capacity(): Promise<number>;
/**
* Called when a node completed its lifecycle and should be removed from the
* queue. Effectively wraps the `remove` call and takes care of the timer.
*
* @param node Drift to decay
*/
private decay;
/**
* Removes an element from the queue. Calls subscribers if there is capacity
* after performing this operation.
*
* @param node Drift to remove
*/
private remove;
/**
* Takes a source element and starts the task for it by calling the worker
* function. Then wraps this task into a drift. Also makes sure that the drift
* removes itself from the queue once it completes, and that the error handler
* is invoked if it fails (rejects).
*
* @param elem Source element
* @param date Date when this drift is created
* @returns The created drift
*/
private toDrift;
/**
* Starts a timer that fires off a timeout after the given period of time.
*
* @param ms Number of milliseconds to wait before the timeout kicks in
*/
private startTimer;
/**
* Performs a timeout event. This removes the head element as well as all
* subsequent drifts with the same date (added in the same millisecond).
*
* The timeout handler is called in sequence for every removed drift.
*/
private timeout;
/**
* Number of pending tasks in the queue. Equivalent to
* `this.pendingTasks().length` (but much more efficient).
*/
get length(): number;
/**
* Creates a snapshot of the queue by computing a list of those elements that
* are currently being processed.
*/
pendingTasks(): Y[];
}