Resolve a list of promises synchronously
Coding Apr 15, 2022
The JavaScript function Promise.all executes promises in parallel. How to write a function to execute and resolve the results synchronously instead.
TLDR; 🔗
In case you just want the code (no judgment!), here it is.
async function allSynchronously<T>(
resolvables: (() => Promise<T>)[]
): Promise<T[]> {
const results = [];
for (const resolvable of resolvables) {
results.push(await resolvable());
}
return results;
}Long version 🔗
Let's assume we want to write a function getBooks(ids: string[]): Promise<Book[]>. For the list of IDs ids: string[] we want to resolve the associated books Book[]. There is an endpoint GET /books/:id. The function getBook(id: string): Promise<Book> makes that endpoint accessible.
interface Book {
id: string;
}
function getBook(id: string): Promise<Book> {
// GET /books/:id
}An implementation of getBooks could use Promise.all to return a promise of books that resolves once the last endpoint call to GET /books/:id returns.
function getBooks(ids: string[]): Promise<Book[]> {
return Promise.all(ids.map(getBook));
}The function Promise.all runs in parallel. This makes it fast. However, there are situations where you want it to run synchronously, one after the other. The API could be rate-limited or you might want to distribute the load.
For these cases, we can write a synchronous version of Promise.all. Such a function can not have the signature allSynchronously<T>(promises: Promise<T>[]): Promise<T[]>. If the function accepted a list of promises as an argument, then at that point in time these promises would already run in parallel. Therefore, the signature accepts a list of zero-argument functions allSynchronously<T>(promises: (() => Promise<T>)[]): Promise<T[]>
async function allSynchronously<T>(
resolvables: (() => Promise<T>)[]
): Promise<T[]> {
const results = [];
for (const resolvable of resolvables) {
results.push(await resolvable());
}
return results;
}Now, an implementation of getBooks can use this function allSynchronously to run the requests synchronously instead of in parallel.
function getBooks(ids: string[]): Promise<Book[]> {
return allSynchronously(ids.map((id) => () => getBook(id)));
}