All files / src batch.ts

100% Statements 20/20
100% Branches 8/8
100% Functions 4/4
100% Lines 18/18

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96              18x                                                         730x                 18x 389x 388x 388x 449x 449x 449x   5x       388x   388x 3x                                                   18x 51x   51x 51x   51x      
import { context } from "./context";
import { UNIQUE_VALUE } from "./utils";
 
export type BatchTask<O extends object = object> = O & {
  batchTask_: () => void;
};
 
export const batchTasks: Set<BatchTask> = /* @__PURE__ */ (() => context.batchTask_)();
 
/**
 * Manually starts a batch of updates without auto-flushing.
 * Use {@link batchFlush} to finish the batch.
 *
 * It is preferred to use the {@link batch} function instead.
 *
 * @category Batch
 * @returns A boolean indicating if this is the first batch trigger.
 *          If true, the caller should call {@link batchFlush} to finish the batch.
 *
 * @example
 * ```ts
 * import { batchStart, batchFlush, writable, compute} from "@embra/reactivity";
 *
 * const count$ = writable(0);
 * const double$ = compute(get => get(count$) * 2);
 *
 * const isFirst = batchStart();
 *
 * count$.set(1);
 * count$.set(2);
 *
 * if (isFirst) {
 *   batchFlush();
 * }
 * ```
 */
export const batchStart = (): boolean => !context.batching_ && (context.batching_ = true);
 
/**
 * Finishes a {@link batchStart} updates.
 *
 * It is preferred to use the {@link batch} function instead.
 *
 * @category Batch
 */
export const batchFlush = (): void => {
  if (context.batching_) {
    let error: unknown = UNIQUE_VALUE;
    for (const task of batchTasks) {
      batchTasks.delete(task);
      try {
        task.batchTask_();
      } catch (e) {
        error = e;
      }
    }
 
    context.batching_ = false;
 
    if (error !== UNIQUE_VALUE) {
      throw error;
    }
  }
};
 
/**
 * Creates a batch of updates. Computations within the batch are deferred until the batch completes.
 *
 * @category Batch
 * @param fn - The function containing updates to batch.
 * @param thisArg - The value to use as `this` when executing `fn`.
 * @returns The result of the function `fn`.
 *
 * @example
 * ```ts
 * import { batch, writable, compute } from "@embra/reactivity";
 *
 * const count$ = writable(0);
 * const double$ = compute(get => get(count$) * 2);
 *
 * batch(() => {
 *   count$.set(1);
 *   count$.set(2);
 * });
 * ```
 */
export const batch = <T>(fn: () => T, thisArg?: any): T => {
  const isFirst = batchStart();
 
  try {
    return fn.call(thisArg);
  } finally {
    isFirst && batchFlush();
  }
};