Skip to content

Instantly share code, notes, and snippets.

@StevenLangbroek
Created July 18, 2020 14:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save StevenLangbroek/cb387dee1cb79f144e819813ae639a1e to your computer and use it in GitHub Desktop.
Save StevenLangbroek/cb387dee1cb79f144e819813ae639a1e to your computer and use it in GitHub Desktop.
// lil' helpers for squeaky clean code 😎
const load = (url: string) =>
fetch(url).then(async r => {
const json = await r.json();
if (!r.ok) {
throw json;
}
return json;
});
const all = Promise.all.bind(Promise);
type LoadManyMode = "sequential" | "parallel";
/**
* This might need a little bit of explanation depending on how much
* you keep up to date with TC39 proposals etc. Let's get the simple stuff
* out of the way first. If you have a function to load a single element,
* you pass it to `loadMany` and get a function back that, given an array
* of whatever you ask for the element by (e.g., a uuid, slug or numeric id),
* will load those one-by-one, as fast as it can.
*
* You can tell it to either load all items in sequence, or in parallel.
*
* It does that by leveraging an Async Generator function. It's a generator function,
* so it can yield multiple values, and since it's async you can `await` elements
* inside of it. Async Generators implement the AsyncIterator interface, which means
* you can iterate over it using `for await (const el of asyncIterator)`. For more details,
* as always MDN has the goods:
*
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of
* @param loader Promise-returning function to get a single element
*/
interface ManyLoader<T> {
(elements: any[], mode: "sequential"): AsyncGenerator<T>;
(elements: any[], mode: "parallel"): AsyncGenerator<T[]>;
}
export const loadMany = <T extends unknown>(
loader: (element: any) => Promise<T>
): ManyLoader<T> => {
return async function*(elements: any[], mode: LoadManyMode = "sequential") {
if (mode === "parallel") {
yield await all(elements.map(loader));
return;
}
for (const el of elements) {
yield await loader(el);
}
};
};
const loadItem = (id: number) => load(`/api/item?id=${id}`);
const loadItems = loadMany<Item>(loadItem);
// use:
loadItems(ids, 'parallel')
loadItems(ids, 'sequential')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment