-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathbatch.ts
More file actions
80 lines (77 loc) · 2.62 KB
/
batch.ts
File metadata and controls
80 lines (77 loc) · 2.62 KB
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
import { createQueue } from './linkedQueue';
import type { SubscribeConsumer } from './subscribeConsumer';
let willProcessQueue = false;
const { add, remove, shift } = createQueue<SubscribeConsumer<any, any>>();
export { add as addToQueue, remove as removeFromQueue };
/**
* Batches multiple changes to stores while calling the provided function,
* preventing derived stores from updating until the function returns,
* to avoid unnecessary recomputations.
*
* @remarks
*
* If a store is updated multiple times in the provided function, existing
* subscribers of that store will only be called once when the provided
* function returns.
*
* Note that even though the computation of derived stores is delayed in most
* cases, some computations of derived stores will still occur inside
* the function provided to batch if a new subscriber is added to a store, because
* calling {@link SubscribableStore.subscribe | subscribe} always triggers a
* synchronous call of the subscriber and because tansu always provides up-to-date
* values when calling subscribers. Especially, calling {@link get} on a store will
* always return the correct up-to-date value and can trigger derived store
* intermediate computations, even inside batch.
*
* It is possible to have nested calls of batch, in which case only the first
* (outer) call has an effect, inner calls only call the provided function.
*
* @param fn - a function that can update stores. Its returned value is
* returned by the batch function.
*
* @example
* Using batch in the following example prevents logging the intermediate "Sherlock Lupin" value.
*
* ```typescript
* const firstName = writable('Arsène');
* const lastName = writable('Lupin');
* const fullName = derived([firstName, lastName], ([a, b]) => `${a} ${b}`);
* fullName.subscribe((name) => console.log(name)); // logs any change to fullName
* batch(() => {
* firstName.set('Sherlock');
* lastName.set('Holmes');
* });
* ```
*/
export const batch = <T>(fn: () => T): T => {
const needsProcessQueue = !willProcessQueue;
willProcessQueue = true;
let success = true;
let res;
let error;
try {
res = fn();
} finally {
if (needsProcessQueue) {
let consumer = shift();
while (consumer) {
try {
consumer.notify();
} catch (e) {
// an error in one consumer should not impact others
if (success) {
// will throw the first error
success = false;
error = e;
}
}
consumer = shift();
}
willProcessQueue = false;
}
}
if (success) {
return res;
}
throw error;
};