Skip to content

Commit

Permalink
Merge pull request #32 from giraugh/feat/resolve-object-settled
Browse files Browse the repository at this point in the history
Add a settleObject util
  • Loading branch information
GRA0007 authored Sep 13, 2023
2 parents c2077bf + 7c19f22 commit 1f6e4b2
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 8 deletions.
5 changes: 5 additions & 0 deletions .changeset/lucky-snakes-flow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@giraugh/tools": minor
---

Add a `settleObject` util that behaves like Promise.allSettled for objects
5 changes: 5 additions & 0 deletions .changeset/modern-doors-melt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@giraugh/tools": patch
---

Allow `resolveObject` to take a `number` or `symbol` as a key
1 change: 1 addition & 0 deletions lib/promises/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './filterAsync'
export * from './mapAsync'
export * from './resolveObject'
export * from './settleObject'
18 changes: 10 additions & 8 deletions lib/promises/resolveObject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,29 @@ export type Resolved<Type> = { [k in keyof Type]: Awaited<Type[k]> }

/**
* Resolve all of the fields of an object in parallel
* @param object An object where every field is a promise
* @param object An object where every field is a promise
* @returns The same object with every field resolved
*
*
* @example await resolveObject({
* a: Promise.resolve('a'),
* b: Promise.resolve('b'),
* b: Promise.resolve('b'),
* }) === { a: 'a', b: 'b' }
*
* @example await resolveObject({
* a: Promise.resolve('a'),
* b: Promise.reject('b'),
* b: Promise.reject('b'),
* }) // throws error
*
* @see {@link settleObject} if you don't want to fail if some promises reject
*/
export const resolveObject = async <T extends Record<string, Promise<any>>>(object: T): Promise<Resolved<T>> => {
// Create promises for each entry
export const resolveObject = async <T extends Record<PropertyKey, Promise<any>>>(object: T): Promise<Resolved<T>> => {
// Create promises for each entry
const entryPromises = Object.entries(object)
.map(([key, promise]) => promise.then(value => [key, value]))

// Resolve the promises
const resolvedEntries = await Promise.all(entryPromises)

// Reconstruct object
return Object.fromEntries(resolvedEntries)
}
Expand Down
61 changes: 61 additions & 0 deletions lib/promises/settleObject.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/**
* Settle all of the fields of an object in parallel
* @param object An object where every field is a promise
* @returns The same object with every field settled
*
* @example await settleObject({
* a: Promise.resolve('a'),
* b: Promise.resolve('b'),
* }) === {
* a: { status: 'fulfilled', value: 'a' },
* b: { status: 'fulfilled', value: 'b' },
* }
*
* @example await settleObject({
* a: Promise.resolve('a'),
* b: Promise.reject('b'),
* }) === {
* a: { status: 'fulfilled', value: 'a' },
* b: { status: 'rejected', reason: 'b' },
* }
*
* @see {@link resolveObject} if you want to fail on any promise rejection
*/
export const settleObject = async <T extends Record<PropertyKey, Promise<unknown>>>(object: T): Promise<{ [k in keyof T]: PromiseSettledResult<T[k]> }> => {
// Turn into entries
const entries = Object.entries(object)

// Settle the promises
const settledValues = await Promise.allSettled(entries.map(e => e[1]))

// Match keys with settled values
const settledEntries = settledValues.map((v, i) => [entries[i][0], v] as const)

// Reconstruct object
return Object.fromEntries(settledEntries) as { [k in keyof T]: PromiseSettledResult<T[k]> }
}

// Tests
if (import.meta.vitest) {
const { it, expect } = import.meta.vitest

it('works for example 1', () => {
expect(settleObject({
a: Promise.resolve('a'),
b: Promise.resolve('b'),
})).resolves.toStrictEqual({
a: { status: 'fulfilled', value: 'a' },
b: { status: 'fulfilled', value: 'b' },
})
})

it('works for example 2', () => {
expect(settleObject({
a: Promise.resolve('a'),
b: Promise.reject('b'),
})).resolves.toStrictEqual({
a: { status: 'fulfilled', value: 'a' },
b: { status: 'rejected', reason: 'b' },
})
})
}

0 comments on commit 1f6e4b2

Please sign in to comment.