Skip to content

Commit 6af4016

Browse files
committed
sort of working
1 parent 3000ab1 commit 6af4016

File tree

1 file changed

+104
-18
lines changed

1 file changed

+104
-18
lines changed

src/async-stores/index.ts

Lines changed: 104 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
type Readable,
55
writable,
66
StartStopNotifier,
7+
readable,
78
} from 'svelte/store';
89
import type {
910
AsyncStoreOptions,
@@ -20,6 +21,7 @@ import {
2021
getStoresArray,
2122
reloadAll,
2223
loadAll,
24+
rebounce,
2325
} from '../utils/index.js';
2426
import { flagStoreCreated, getStoreTestingMode, logError } from '../config.js';
2527

@@ -66,44 +68,128 @@ export const asyncWritable = <S extends Stores, T>(
6668
flagStoreCreated();
6769
const { reloadable, trackState, initial } = options;
6870

69-
const loadState = trackState
70-
? writable<LoadState>(getLoadState('LOADING'))
71-
: undefined;
72-
const setState = (state: State) => loadState?.set(getLoadState(state));
71+
const rebouncedMappingLoad = rebounce(mappingLoadFunction);
72+
73+
const loadState = writable<LoadState>(getLoadState('LOADING'));
74+
const setState = (state: State) => loadState.set(getLoadState(state));
7375

7476
// flag marking whether store is ready for updates from subscriptions
7577
let ready = false;
78+
let changeReceived = false;
7679

7780
// most recent call of mappingLoadFunction, including resulting side effects
7881
// (updating store value, tracking state, etc)
7982
let currentLoadPromise: Promise<T>;
83+
let resolveCurrentLoad: (value: T | PromiseLike<T>) => void;
84+
let rejectCurrentLoad: (reason: Error) => void;
85+
86+
const setCurrentLoadPromise = () => {
87+
currentLoadPromise = new Promise((resolve, reject) => {
88+
resolveCurrentLoad = resolve;
89+
rejectCurrentLoad = reject;
90+
});
91+
};
8092

81-
let latestLoadAndSet: () => Promise<T>;
93+
let parentValues: StoresValues<S>;
94+
95+
const mappingLoadThenSet = async (setStoreValue) => {
96+
if (get(loadState).isSettled) {
97+
setCurrentLoadPromise();
98+
setState('RELOADING');
99+
}
82100

83-
const tryLoadValue = async (values: StoresValues<S>) => {
84101
try {
85-
return await mappingLoadFunction(values);
102+
const finalValue = await rebouncedMappingLoad(parentValues);
103+
setStoreValue(finalValue);
104+
resolveCurrentLoad(finalValue);
105+
setState('LOADED');
86106
} catch (e) {
87107
if (e.name !== 'AbortError') {
88-
logError(e);
89108
setState('ERROR');
109+
rejectCurrentLoad(e);
90110
}
91-
throw e;
92111
}
93112
};
94113

95-
const loadThenSet = async (set) => {};
114+
const onFirstSubscription: StartStopNotifier<T> = (setStoreValue) => {
115+
setCurrentLoadPromise();
96116

97-
const onFirstSubscribtion: StartStopNotifier<T> = async (set) => {
98-
(async () => {
99-
const values = await loadAll(stores);
100-
ready = true;
101-
tryLoadValue(values);
102-
})();
103-
const values = await loadAll();
117+
const initialLoad = async () => {
118+
try {
119+
parentValues = await loadAll(stores);
120+
ready = true;
121+
changeReceived = false;
122+
mappingLoadThenSet(setStoreValue);
123+
} catch (error) {
124+
rejectCurrentLoad(error);
125+
}
126+
};
127+
initialLoad();
128+
129+
const parentUnsubscribers = getStoresArray(stores).map((store, i) =>
130+
store.subscribe((value) => {
131+
changeReceived = true;
132+
if (Array.isArray(stores)) {
133+
parentValues[i] = value;
134+
} else {
135+
parentValues = value as any;
136+
}
137+
if (ready) {
138+
mappingLoadThenSet(setStoreValue);
139+
}
140+
})
141+
);
142+
143+
// called on losing last subscriber
144+
return () => {
145+
parentUnsubscribers.map((unsubscriber) => unsubscriber());
146+
};
147+
};
148+
149+
const thisStore = writable(initial, onFirstSubscription);
150+
151+
const subscribe = thisStore.subscribe;
152+
const load = async () => {
153+
const dummyUnsubscribe = thisStore.subscribe(() => {
154+
/* no-op */
155+
});
156+
try {
157+
const result = await currentLoadPromise;
158+
dummyUnsubscribe();
159+
return result;
160+
} catch (error) {
161+
dummyUnsubscribe();
162+
throw error;
163+
}
164+
};
165+
const reload = async (visitedMap?: VisitedMap) => {
166+
ready = false;
167+
changeReceived = false;
168+
setCurrentLoadPromise();
169+
setState('RELOADING');
170+
171+
const visitMap = visitedMap ?? new WeakMap();
172+
await reloadAll(stores, visitMap);
173+
ready = true;
174+
if (changeReceived || reloadable) {
175+
mappingLoadThenSet(thisStore.set);
176+
} else {
177+
resolveCurrentLoad(get(thisStore));
178+
}
179+
return currentLoadPromise;
104180
};
105-
};
106181

182+
return {
183+
get store() {
184+
return this;
185+
},
186+
subscribe,
187+
load,
188+
reload,
189+
set: () => Promise.resolve(),
190+
update: () => Promise.resolve(),
191+
};
192+
};
107193
/**
108194
* Generate a Loadable store that is considered 'loaded' after resolving asynchronous behavior.
109195
* This asynchronous behavior may be derived from the value of parent Loadable or non Loadable stores.

0 commit comments

Comments
 (0)