vchikalkin e623ed0726 utils/mobx: add queueReaction
queue load-kp & subsidy reactions
2023-06-06 13:35:15 +03:00

92 lines
2.4 KiB
TypeScript

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<T, FireImmediately extends boolean = false>(
disposeExpression: () => boolean,
expression: (r: IReactionPublic) => T,
effect: (
arg: T,
prev: FireImmediately extends true ? T | undefined : T,
r: IReactionPublic
) => void,
reactionOpts?: IReactionOptions<T, FireImmediately>
) {
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<T, FireImmediately extends boolean = false>(
expression: (r: IReactionPublic) => T,
effect: (
arg: T,
prev: FireImmediately extends true ? T | undefined : T,
r: IReactionPublic
) => void,
{ wait, ...reactionOpts }: IReactionOptions<T, FireImmediately> & { wait: number }
): IReactionDisposer {
const debouncedEffect = debounce({ delay: wait }, effect);
return reaction(expression, debouncedEffect, reactionOpts);
}
const queue = new Queue(1, 10);
export function queueReaction<T, FireImmediately extends boolean = false>(
expression: (r: IReactionPublic) => T,
effect: (
arg: T,
prev: FireImmediately extends true ? T | undefined : T,
r: IReactionPublic
) => Promise<void>,
{
priority,
name,
...reactionOpts
}: IReactionOptions<T, FireImmediately> & { 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);
}