UNPKG

@stackbit/utils

Version:
73 lines (64 loc) 2.32 kB
import _ from 'lodash'; import { deferredPromise, DeferredPromise } from './promise-utils'; type EnqueuedJob = { jobId: string; jobFn: () => Promise<any>; deferred: DeferredPromise<unknown>; nextJobFn: (() => Promise<any>) | null; }; export class Worker { private promise: Promise<any>; private enqueued: EnqueuedJob[]; constructor() { this.promise = Promise.resolve(); this.enqueued = []; } schedule(jobFn: () => Promise<any>): Promise<any> { return new Promise((resolve, reject) => { this.promise = this.promise.finally(() => { const res = jobFn(); if (res && typeof _.get(res, 'then') === 'function') { return res.then(resolve).catch(reject); } else { resolve(res); } }); }); } /** * Run one after another 2 calls of same jobFn, 3rd and next calls will be waiting for finishing 2nd and * respond all together * In case there are another jobFn called on the same worker - 2nd round of calls would be postponed to process next call * to prevent dead lock * @see https://user-images.githubusercontent.com/97896/90123842-1fea2f80-dd68-11ea-8462-d4f1cdc1749b.png * @param {String} jobId * @param {Function} jobFn * @returns {Promise} */ enqueueOnce(jobId: string, jobFn: () => Promise<any>): Promise<any> { let enqueued = this.enqueued.find((obj) => obj.jobId === jobId); if (enqueued) { if (!enqueued.nextJobFn) { enqueued.nextJobFn = jobFn; } return enqueued.deferred.promise; } enqueued = { jobId, jobFn, deferred: deferredPromise(), nextJobFn: null }; this.enqueued.push(enqueued); return this.schedule(jobFn).finally(() => { if (!enqueued) { return; } const index = this.enqueued.indexOf(enqueued); this.enqueued.splice(index, 1); if (enqueued.nextJobFn) { this.enqueueOnce(enqueued.jobId, enqueued.nextJobFn).then(enqueued.deferred.resolve).catch(enqueued.deferred.reject); } }); } }