UNPKG

3.82 kBJavaScriptView Raw
1import { noop, safe_not_equal, run_all, is_function } from '../internal';
2
3/**
4 * Creates a `Readable` store that allows reading by subscription.
5 * @param value initial value
6 * @param {StartStopNotifier}start start and stop notifications for subscriptions
7 */
8function readable(value, start) {
9 return {
10 subscribe: writable(value, start).subscribe,
11 };
12}
13/**
14 * Create a `Writable` store that allows both updating and reading by subscription.
15 * @param {*=}value initial value
16 * @param {StartStopNotifier=}start start and stop notifications for subscriptions
17 */
18function writable(value, start = noop) {
19 let stop;
20 const subscribers = [];
21 function set(new_value) {
22 if (safe_not_equal(value, new_value)) {
23 value = new_value;
24 if (!stop) {
25 return; // not ready
26 }
27 subscribers.forEach((s) => s[1]());
28 subscribers.forEach((s) => s[0](value));
29 }
30 }
31 function update(fn) {
32 set(fn(value));
33 }
34 function subscribe(run, invalidate = noop) {
35 const subscriber = [run, invalidate];
36 subscribers.push(subscriber);
37 if (subscribers.length === 1) {
38 stop = start(set) || noop;
39 }
40 run(value);
41 return () => {
42 const index = subscribers.indexOf(subscriber);
43 if (index !== -1) {
44 subscribers.splice(index, 1);
45 }
46 if (subscribers.length === 0) {
47 stop();
48 stop = null;
49 }
50 };
51 }
52 return { set, update, subscribe };
53}
54/**
55 * Derived value store by synchronizing one or more readable stores and
56 * applying an aggregation function over its input values.
57 * @param {Stores} stores input stores
58 * @param {function(Stores=, function(*)=):*}fn function callback that aggregates the values
59 * @param {*=}initial_value when used asynchronously
60 */
61function derived(stores, fn, initial_value) {
62 const single = !Array.isArray(stores);
63 const stores_array = single
64 ? [stores]
65 : stores;
66 const auto = fn.length < 2;
67 const invalidators = [];
68 const store = readable(initial_value, (set) => {
69 let inited = false;
70 const values = [];
71 let pending = 0;
72 let cleanup = noop;
73 const sync = () => {
74 if (pending) {
75 return;
76 }
77 cleanup();
78 const result = fn(single ? values[0] : values, set);
79 if (auto) {
80 set(result);
81 }
82 else {
83 cleanup = is_function(result) ? result : noop;
84 }
85 };
86 const unsubscribers = stores_array.map((store, i) => store.subscribe((value) => {
87 values[i] = value;
88 pending &= ~(1 << i);
89 if (inited) {
90 sync();
91 }
92 }, () => {
93 run_all(invalidators);
94 pending |= (1 << i);
95 }));
96 inited = true;
97 sync();
98 return function stop() {
99 run_all(unsubscribers);
100 cleanup();
101 };
102 });
103 return {
104 subscribe(run, invalidate = noop) {
105 invalidators.push(invalidate);
106 const unsubscribe = store.subscribe(run, invalidate);
107 return () => {
108 const index = invalidators.indexOf(invalidate);
109 if (index !== -1) {
110 invalidators.splice(index, 1);
111 }
112 unsubscribe();
113 };
114 }
115 };
116}
117/**
118 * Get the current value from a store by subscribing and immediately unsubscribing.
119 * @param store readable
120 */
121function get(store) {
122 let value;
123 store.subscribe((_) => value = _)();
124 return value;
125}
126
127export { readable, writable, derived, get };