Skip to content

Commit 10e1f85

Browse files
committed
feat: add Task.fromPromises and fix Task.prototype.chain error handling
1 parent ded8a61 commit 10e1f85

File tree

3 files changed

+70
-1
lines changed

3 files changed

+70
-1
lines changed

docs/task-static.md

+24
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,30 @@ type fromPromise = <S>(maybePromise: S | Promise<S>) => Task<unknown, S>
260260
{% endtab %}
261261
{% endtabs %}
262262
263+
## fromPromises
264+
265+
Converts an array of Promises into an array of Tasks and joins them with Task.all
266+
267+
Promise's do not track an error type (one of the reasons Tasks are more powerful) so the resulting Task is unable to infer the error type as well. It is recommended to pass it in as a generic.
268+
269+
{% tabs %}
270+
{% tab title="Usage" %}
271+
272+
```typescript
273+
const task: Task<unknown, Response> = Task.fromPromises(fetch(URL))
274+
```
275+
276+
{% endtab %}
277+
278+
{% tab title="Type Definition" %}
279+
280+
```typescript
281+
type fromPromises = <S>(promises: Array<Promise<S>>) => Task<unknown, S[]>
282+
```
283+
284+
{% endtab %}
285+
{% endtabs %}
286+
263287
## fromLazyPromise
264288
265289
Given a function which returns a Promise, turn that into a Task. This allows the Promise not to start until the Task forks (following the lazy philosophy of the rest of the library). This also means if two tasks chain from this one, the promise creating function will be called twice. See `onlyOnce` if you wish to avoid this.

src/Task/Task.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,14 @@ export const fromPromise = <S>(
211211
? new Task((reject, resolve) => maybePromise.then(resolve, reject))
212212
: of(maybePromise)
213213

214+
/**
215+
* Given an array of promises, create a Task which relies on it.
216+
* @param promise The promises we will gather the success from.
217+
*/
218+
export const fromPromises = <S>(
219+
promises: Array<Promise<S>>,
220+
): Task<unknown, S[]> => all(promises.map(fromPromise))
221+
214222
/**
215223
* Take a function which generates a promise and lazily execute it.
216224
* @param getPromise The getter function
@@ -690,6 +698,7 @@ export class Task<E, S> implements PromiseLike<S> {
690698
public static firstSuccess = firstSuccess
691699
public static never = never
692700
public static fromPromise = fromPromise
701+
public static fromPromises = fromPromises
693702
public static fromLazyPromise = fromLazyPromise
694703
public static race = race
695704
public static external = external
@@ -756,7 +765,7 @@ export class Task<E, S> implements PromiseLike<S> {
756765
return this.toPromise().then(onfulfilled, onrejected)
757766
}
758767

759-
public chain<S2>(fn: (result: S) => Task<E, S2>): Task<E, S2> {
768+
public chain<E2, S2>(fn: (result: S) => Task<E2, S2>): Task<E | E2, S2> {
760769
return chain(fn, this)
761770
}
762771

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { fromPromises } from "../Task"
2+
import { ERROR_RESULT, SUCCESS_RESULT } from "./util"
3+
4+
describe("fromPromisea", () => {
5+
test("should succeed when all promises succeeds", async () => {
6+
const resolve = jest.fn()
7+
const reject = jest.fn()
8+
9+
const promiseA = Promise.resolve(SUCCESS_RESULT)
10+
const promiseB = Promise.resolve(SUCCESS_RESULT)
11+
12+
fromPromises([promiseA, promiseB]).fork(reject, resolve)
13+
14+
await promiseA.catch(() => void 0)
15+
await promiseB.catch(() => void 0)
16+
17+
expect(resolve).toBeCalledWith([SUCCESS_RESULT, SUCCESS_RESULT])
18+
expect(reject).not.toBeCalled()
19+
})
20+
21+
test("should fail when a promise fails", async () => {
22+
const resolve = jest.fn()
23+
const reject = jest.fn()
24+
25+
const promiseA = Promise.resolve(SUCCESS_RESULT)
26+
const promiseB = Promise.reject(ERROR_RESULT)
27+
28+
fromPromises([promiseA, promiseB]).fork(reject, resolve)
29+
30+
await promiseA.catch(() => void 0)
31+
await promiseB.catch(() => void 0)
32+
33+
expect(resolve).not.toBeCalled()
34+
expect(reject).toBeCalledWith(ERROR_RESULT)
35+
})
36+
})

0 commit comments

Comments
 (0)