import { Queue } from 'async-await-queue'; import type { IReactionDisposer, IReactionOptions, IReactionPublic } from 'mobx'; import { reaction } from 'mobx'; import { debounce, omit } from 'radash'; export function disposableReaction( disposeExpression: () => boolean, expression: (r: IReactionPublic) => T, effect: ( arg: T, prev: FireImmediately extends true ? T | undefined : T, r: IReactionPublic ) => void, reactionOpts?: IReactionOptions ) { let disposer: IReactionDisposer | undefined; if (!disposeExpression()) { disposer = reaction(expression, effect, reactionOpts); } function cleanDisposer() { disposer = undefined; } reaction(disposeExpression, (mustBeDisposed) => { if (mustBeDisposed) { if (disposer !== undefined) disposer(); cleanDisposer(); } else { setTimeout(() => { disposer = reaction( expression, effect, reactionOpts ? omit(reactionOpts, ['fireImmediately']) : undefined ); }, 100); } }); } export function debouncedReaction( expression: (r: IReactionPublic) => T, effect: ( arg: T, prev: FireImmediately extends true ? T | undefined : T, r: IReactionPublic ) => void, { wait, ...reactionOpts }: IReactionOptions & { wait: number } ): IReactionDisposer { const debouncedEffect = debounce({ delay: wait }, effect); return reaction(expression, debouncedEffect, reactionOpts); } const queue = new Queue(1, 10); export function queueReaction( expression: (r: IReactionPublic) => T, effect: ( arg: T, prev: FireImmediately extends true ? T | undefined : T, r: IReactionPublic ) => Promise, { priority, name, ...reactionOpts }: IReactionOptions & { name: string; priority: number } ): IReactionDisposer { const me = Symbol(name); async function queueEffect( arg: T, prev: FireImmediately extends true ? T | undefined : T, r: IReactionPublic ) { await queue.wait(me, priority); try { await effect(arg, prev, r); } catch (error) { // eslint-disable-next-line no-console console.error(error); } finally { queue.end(me); } } return reaction(expression, queueEffect, reactionOpts); }