Skip to content

Commit 3805421

Browse files
committed
solve diamond dependencies (#2660)
1 parent 04162b9 commit 3805421

File tree

2 files changed

+77
-13
lines changed

2 files changed

+77
-13
lines changed

src/runtime/store/index.ts

+49-13
Original file line numberDiff line numberDiff line change
@@ -112,12 +112,12 @@ type StoresValues<T> = T extends Readable<infer U> ? U :
112112
* applying an aggregation function over its input values.
113113
* @param {Stores} stores input stores
114114
* @param {function(Stores=, function(*)=):*}fn function callback that aggregates the values
115-
* @param {*=}initial_value when used asynchronously
115+
* @param {*=}value initial value, when used asynchronously
116116
*/
117117
export function derived<T, S extends Stores>(
118118
stores: S,
119119
fn: (values: StoresValues<S>, set?: Subscriber<T>) => T | Unsubscriber | void,
120-
initial_value?: T,
120+
value?: T,
121121
): Readable<T> {
122122

123123
const single = !Array.isArray(stores);
@@ -127,12 +127,28 @@ export function derived<T, S extends Stores>(
127127

128128
const auto = fn.length < 2;
129129

130-
return readable(initial_value, (set) => {
131-
let inited = false;
130+
const subscribers: Array<SubscribeInvalidateTuple<T>> = [];
131+
let unsubscribers;
132+
let cleanup = noop;
133+
let running = false;
134+
135+
function invalidate() {
136+
subscribers.forEach(subscriber => subscriber[1]());
137+
}
138+
139+
function set(current_value) {
140+
value = current_value;
141+
if (running) {
142+
invalidate();
143+
subscribers.forEach(subscriber => subscriber[0](value));
144+
}
145+
}
146+
147+
function start() {
132148
const values: StoresValues<S> = [] as StoresValues<S>;
133149

134150
let pending = 0;
135-
let cleanup = noop;
151+
running = false;
136152

137153
const sync = () => {
138154
if (pending) {
@@ -147,27 +163,47 @@ export function derived<T, S extends Stores>(
147163
}
148164
};
149165

150-
const unsubscribers = stores_array.map((store, i) => store.subscribe(
166+
unsubscribers = stores_array.map((store, i) => store.subscribe(
151167
(value) => {
152168
values[i] = value;
153169
pending &= ~(1 << i);
154-
if (inited) {
170+
if (running) {
155171
sync();
156172
}
157173
},
158174
() => {
175+
invalidate();
159176
pending |= (1 << i);
160177
}),
161178
);
162179

163-
inited = true;
164180
sync();
181+
running = true;
182+
}
165183

166-
return function stop() {
167-
run_all(unsubscribers);
168-
cleanup();
169-
};
170-
});
184+
function stop() {
185+
run_all(unsubscribers);
186+
cleanup();
187+
}
188+
189+
return {
190+
subscribe(run: Subscriber<T>, invalidate: Invalidater<T> = noop): Unsubscriber {
191+
const subscriber: SubscribeInvalidateTuple<T> = [run, invalidate];
192+
subscribers.push(subscriber);
193+
if (subscribers.length === 1) start();
194+
run(value);
195+
196+
return () => {
197+
const index = subscribers.indexOf(subscriber);
198+
if (index !== -1) {
199+
subscribers.splice(index, 1);
200+
}
201+
if (subscribers.length === 0) {
202+
stop();
203+
}
204+
};
205+
}
206+
};
171207
}
172208

173209
/**

test/store/index.ts

+28
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,34 @@ describe('store', () => {
189189
unsubscribe();
190190
});
191191

192+
it('prevents diamond dependency problem', () => {
193+
const count = writable(0);
194+
const values = [];
195+
196+
const a = derived(count, $count => {
197+
return 'a' + $count;
198+
});
199+
200+
const b = derived(count, $count => {
201+
return 'b' + $count;
202+
});
203+
204+
const combined = derived([a, b], ([a, b]) => {
205+
return a + b;
206+
});
207+
208+
const unsubscribe = combined.subscribe(v => {
209+
values.push(v);
210+
});
211+
212+
assert.deepEqual(values, ['a0b0']);
213+
214+
count.set(1);
215+
assert.deepEqual(values, ['a0b0', 'a1b1']);
216+
217+
unsubscribe();
218+
});
219+
192220
it('is updated with safe_not_equal logic', () => {
193221
const arr = [0];
194222

0 commit comments

Comments
 (0)