Skip to content

Commit

Permalink
Add promiseStore to easily wrap a Promise as a store. Useful for Svel…
Browse files Browse the repository at this point in the history
…teKit streamed data handling
  • Loading branch information
techniq committed Aug 25, 2023
1 parent 03968f7 commit 7b2f662
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/twenty-paws-play.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'svelte-ux': patch
---

Add promiseStore to easily wrap a Promise as a store. Useful for SvelteKit streamed data handling
1 change: 1 addition & 0 deletions src/lib/stores/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export { default as localStore } from './localStore';
export { default as mapStore } from './mapStore';
export * from './matchMedia';
export { default as paginationStore } from './paginationStore';
export * from './promiseStore';
export { default as selectionStore } from './selectionStore';
export { default as tableOrderStore } from './tableOrderStore';
export { default as timerStore } from './timerStore';
Expand Down
65 changes: 65 additions & 0 deletions src/lib/stores/promiseStore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { writable } from 'svelte/store';

export type PromiseStoreData<T> = {
loading: boolean;
data?: T;
error?: Error;
aborted?: boolean;
};

const WAITING = Symbol();

/**
* Add promiseStore to easily wrap a Promise as a store. Useful for SvelteKit streamed data handling
*/
export function promiseStore<T>(initialValue?: Promise<T> | null) {
let currentPromise: Promise<T> | null = null;

let store = writable<PromiseStoreData<T>>({
loading: false,
aborted: false,
});

let result = {
subscribe: store.subscribe,

async setPromise(promise: Promise<T> | undefined) {
if (!promise) {
currentPromise = null;
store.set({
data: undefined,
loading: false,
aborted: false,
});
return;
}

// If promise is already resolved, update immediately
const resolved = await Promise.race([promise, Promise.resolve(WAITING)]);
if (resolved === WAITING) {
store.update((s) => ({ ...s, loading: true }));
}

if (promise !== currentPromise) {
currentPromise = promise;
try {
const data = await promise;
if (currentPromise === promise) {
store.set({ data, loading: false, aborted: false });
}
} catch (err) {
if (currentPromise === promise) {
const error = err as Error;
store.set({ error, aborted: error.name === 'AbortError', loading: false });
}
}
}
},
};

if (initialValue) {
result.setPromise(initialValue);
}

return result;
}

1 comment on commit 7b2f662

@vercel
Copy link

@vercel vercel bot commented on 7b2f662 Aug 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.