Codibre/remembered

View on GitHub
src/pacer.ts

Summary

Maintainability
A
35 mins
Test Coverage
A
100%
import { RememberedConfig } from './remembered-config';
import Fifo = require('fast-fifo');
import { delay } from './delay';
import { performance } from 'perf_hooks';

export class Pacer<TResponse = unknown, TKey = string> {
    private purgeTask: PromiseLike<void> | undefined;
    private toPurge = new Fifo<{
        purgeTime: number;
        payload: TKey;
        callback?: (payload: TKey) => void;
    }>();

    constructor(
        private config: RememberedConfig<TResponse, TKey>,
        private run: (payload: TKey) => any,
    ) {}

    private getTtl(
        payload: TKey,
        ttl: number | undefined,
        response: TResponse,
    ): number {
        if (ttl !== undefined) return ttl;

        return typeof this.config.ttl === 'number'
            ? this.config.ttl
            : this.config.ttl(payload, response);
    }

    schedulePurge(
        payload: TKey,
        ttl: number | undefined,
        response: TResponse,
        callback?: (payload: TKey) => void,
    ) {
        const now = performance.now();
        ttl = this.getTtl(payload, ttl, response);

        if (ttl === 0) {
            this.run(payload);
            return;
        }

        const purgeTime = now + ttl;

        this.toPurge.push({ purgeTime, payload, callback });
        if (!this.purgeTask) {
            this.purgeTask = this.wait();
        }
    }

    private async wait(): Promise<void> {
        const current = this.toPurge.shift();
        if (current) {
            const waiting = current.purgeTime - performance.now();
            if (waiting > 0) {
                await delay(waiting);
            }
            this.run(current.payload);
            if (current.callback) {
                current.callback(current.payload);
            }
            return this.wait();
        } else {
            this.purgeTask = undefined;
        }
    }
}