File size: 2,368 Bytes
dbb1bf9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
97
98
99
100
101
102
103
type LayoutBatchCallback = () => void;

export type CancelLayoutBatch = () => void;

interface QueuedCallback {
  callback: LayoutBatchCallback;
  cancelled: boolean;
}

const measureQueue: QueuedCallback[] = [];
const mutateQueue: QueuedCallback[] = [];

let scheduled = false;
let flushing = false;

function requestFrame(callback: FrameRequestCallback): void {
  const raf =
    typeof window !== 'undefined' && typeof window.requestAnimationFrame === 'function'
      ? window.requestAnimationFrame.bind(window)
      : typeof globalThis.requestAnimationFrame === 'function'
        ? globalThis.requestAnimationFrame.bind(globalThis)
        : undefined;

  if (raf) {
    raf(callback);
    return;
  }

  setTimeout(() => callback(Date.now()), 0);
}

function scheduleFlush(): void {
  if (scheduled || flushing) return;
  scheduled = true;
  requestFrame(flushQueues);
}

function enqueue(queue: QueuedCallback[], callback: LayoutBatchCallback): CancelLayoutBatch {
  const entry: QueuedCallback = { callback, cancelled: false };
  queue.push(entry);
  scheduleFlush();
  return () => {
    entry.cancelled = true;
  };
}

function reportLayoutBatchError(error: unknown): void {
  if (typeof globalThis.reportError === 'function') {
    globalThis.reportError(error);
    return;
  }

  setTimeout(() => {
    throw error;
  }, 0);
}

function flushQueue(queue: QueuedCallback[]): void {
  const errors: unknown[] = [];

  for (const entry of queue) {
    if (entry.cancelled) continue;
    try {
      entry.callback();
    } catch (error) {
      errors.push(error);
    }
  }

  for (const error of errors) reportLayoutBatchError(error);
}

function flushQueues(): void {
  scheduled = false;
  flushing = true;

  try {
    const measures = measureQueue.splice(0);
    flushQueue(measures);

    const mutates = mutateQueue.splice(0);
    flushQueue(mutates);
  } finally {
    flushing = false;
    if (measureQueue.length > 0 || mutateQueue.length > 0) scheduleFlush();
  }
}

export function __resetForTest(): void {
  measureQueue.length = 0;
  mutateQueue.length = 0;
  scheduled = false;
  flushing = false;
}

export function measure(callback: LayoutBatchCallback): CancelLayoutBatch {
  return enqueue(measureQueue, callback);
}

export function mutate(callback: LayoutBatchCallback): CancelLayoutBatch {
  return enqueue(mutateQueue, callback);
}