Skip to content

Commit

Permalink
feat: add Task.fromPromises and fix Task.prototype.chain error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
tdreyno committed Jan 8, 2021
1 parent ded8a61 commit 10e1f85
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 1 deletion.
24 changes: 24 additions & 0 deletions docs/task-static.md
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,30 @@ type fromPromise = <S>(maybePromise: S | Promise<S>) => Task<unknown, S>
{% endtab %}
{% endtabs %}
## fromPromises
Converts an array of Promises into an array of Tasks and joins them with Task.all
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.
{% tabs %}
{% tab title="Usage" %}
```typescript
const task: Task<unknown, Response> = Task.fromPromises(fetch(URL))
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type fromPromises = <S>(promises: Array<Promise<S>>) => Task<unknown, S[]>
```
{% endtab %}
{% endtabs %}
## fromLazyPromise
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.
Expand Down
11 changes: 10 additions & 1 deletion src/Task/Task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,14 @@ export const fromPromise = <S>(
? new Task((reject, resolve) => maybePromise.then(resolve, reject))
: of(maybePromise)

/**
* Given an array of promises, create a Task which relies on it.
* @param promise The promises we will gather the success from.
*/
export const fromPromises = <S>(
promises: Array<Promise<S>>,
): Task<unknown, S[]> => all(promises.map(fromPromise))

/**
* Take a function which generates a promise and lazily execute it.
* @param getPromise The getter function
Expand Down Expand Up @@ -690,6 +698,7 @@ export class Task<E, S> implements PromiseLike<S> {
public static firstSuccess = firstSuccess
public static never = never
public static fromPromise = fromPromise
public static fromPromises = fromPromises
public static fromLazyPromise = fromLazyPromise
public static race = race
public static external = external
Expand Down Expand Up @@ -756,7 +765,7 @@ export class Task<E, S> implements PromiseLike<S> {
return this.toPromise().then(onfulfilled, onrejected)
}

public chain<S2>(fn: (result: S) => Task<E, S2>): Task<E, S2> {
public chain<E2, S2>(fn: (result: S) => Task<E2, S2>): Task<E | E2, S2> {
return chain(fn, this)
}

Expand Down
36 changes: 36 additions & 0 deletions src/Task/__tests__/fromPromises.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { fromPromises } from "../Task"
import { ERROR_RESULT, SUCCESS_RESULT } from "./util"

describe("fromPromisea", () => {
test("should succeed when all promises succeeds", async () => {
const resolve = jest.fn()
const reject = jest.fn()

const promiseA = Promise.resolve(SUCCESS_RESULT)
const promiseB = Promise.resolve(SUCCESS_RESULT)

fromPromises([promiseA, promiseB]).fork(reject, resolve)

await promiseA.catch(() => void 0)
await promiseB.catch(() => void 0)

expect(resolve).toBeCalledWith([SUCCESS_RESULT, SUCCESS_RESULT])
expect(reject).not.toBeCalled()
})

test("should fail when a promise fails", async () => {
const resolve = jest.fn()
const reject = jest.fn()

const promiseA = Promise.resolve(SUCCESS_RESULT)
const promiseB = Promise.reject(ERROR_RESULT)

fromPromises([promiseA, promiseB]).fork(reject, resolve)

await promiseA.catch(() => void 0)
await promiseB.catch(() => void 0)

expect(resolve).not.toBeCalled()
expect(reject).toBeCalledWith(ERROR_RESULT)
})
})

0 comments on commit 10e1f85

Please sign in to comment.