# Task overview

This namespace contains various utilities for Tasks.

A Task represents a lazy asynchroneous action.

# Summary

# Types

# Task

type Task<T = any> = PromiseLike<T> & { _tag: 'Task' }

# Functions

# Task.isTask

# Description

Check if variable is a task

<A>(value: unknown) => value is Task<A>

# Task.of

# Description

Creates a resolving Task

(): Task<void>
<A>(value: A): Task<A>

# Task.resolve

# Description

Creates a resolving Task

(): Task<void>
<A>(value: A): Task<A>

# Task.reject

# Description

Creates a rejecting Task

(value: unknown) => Task<never>

# Task.run

# Description

Runs a Task

<A>(task: Task<A>) => Promise<A>

# Task.sleep

# Description

Creates a Task that waits a specific amount of milliseconds before resolving.

(ms: number) => Task<void>

# References

  • Task.delay

# Task.delay

# Description

Taps the Task and delays the resolving by a specific amount of milliseconds.

(ms: number) => <A>(task: Task<A>) => Task<A>

# Example

const result = await pipe(
  Task.of(42),
  Task.delay(1000), // Waits 1 second before resolving 42
  Task.run
)

expect(result).toBe(42)

# References

  • Task.sleep

# Task.map

# Description

Maps over the value of a resolving Task.

<A, B>(fn: (value: A) => B) => (task: Task<A>) => Task<B>

# Example

const result = await pipe(
  Task.of(1),
  Task.map(a => a + 1),
  Task.run
)

expect(result).toBe(2)

# References

  • Task.mapError
  • Task.chain

# Task.mapError

# Description

Maps over the error of a rejecting Task.

<A>(fn: (err: unknown) => unknown) => (task: Task<A>) => Task<A>

# Example

try {
  await pipe(
    Task.reject(Err.of('some error')),
    Task.mapError(Err.chain('could not execute xxxx')),
    Task.run
  )
} catch (err) {
  expect(err.message).toBe("could not execute xxxx: some error")
}

# References

  • Task.map
  • Task.catchError

# Task.chain

# Description

Chain a Task or any other PromiseLike to execute when the Task resolves.

<A, B>(fn: (value: A) => PromiseLike<B>) => (task: Task<A>) => Task<B>

# Example

const result = await pipe(
  Task.of(1),
  Task.chain(a => pipe(
    Task.of(a + 1),
    Task.delay(1000)
  )),
  Task.run
)

expect(result).toBe(2)

# References

  • Task.map
  • Task.catchError

# Task.catchError

# Description

Chain another Task to execute when the Task rejects.

<A, B>(fn: (err: unknown) => PromiseLike<B>) => (task: Task<A>) => Task<A | B>

# Example

const result = await pipe(
  Task.reject(Err.of('some error', { name: 'SomeError' })),
  Task.catchError(err =>
    pipe(err, Err.hasName('SomeError'))
     ? Task.of('success')
     : Task.reject('reject')
  ),
  Task.run
)

expect(result).toBe('success')

# References

  • Task.mapError
  • Task.chain

# Task.tap

# Description

When the tasks resolves, execute a side-effect on the current value without modifying the value

<A, B>(fn: (value: A) => B | PromiseLike<B>) => (task: Task<A>) => Task<A>

# Example

const result = await pipe(
  Task.of(42),
  Task.tap(value => console.log('received value', value)),
  Task.map(a => a + 1)
)

expect(result).toBe(43)

# Task.tapError

# Description

When the task rejects, execute a side-effect on the current error without modifying the error

<A, B>(fn: (value: A) => B | PromiseLike<B>) => (task: Task<A>) => Task<A>

# Example

const [, error] = await pipe(
  Task.reject(new Error('Internal error')),
  Task.tapError(err => console.error('An error occured', err)),
  Task.tryCatch,
  Task.map(Result.mapError(Err.toError)),
  Task.map(Result.tuple)
)

expect(error?.message).toBe('Internal error')

# Task.all

# Description

Combine an array of Tasks into a single Task. This function will execute all tasks in parallel.

<A>(tasks: Array<Task<A>>) => Task<Array<A>>

# Example

const results = await pipe(
  [Task.of(1), Task.of(2), Task.of(3)],
  Task.all,
  Task.run
)

expect(results).toEqual([1,2,3])

# References

  • Task.sequence
  • Task.concurrent

# Task.sequence

# Description

Combine an array of Tasks into a single Task. This function will execute all tasks in sequence.

<A>(tasks: Array<Task<A>>) => Task<Array<A>>

# Example

const results = await pipe(
  [Task.of(1), Task.of(2), Task.of(3)],
  Task.sequence,
  Task.run
)

expect(results).toEqual([1,2,3])

# References

  • Task.all
  • Task.concurrent

# Task.concurrent

# Description

Combine an array of Tasks into a single Task. This function will execute all tasks in concurrency.

(concurrency: number) => <A>(tasks: Array<Task<A>>) => Task<Array<A>>

# Example

const results = await pipe(
  [Task.of(1), Task.of(2), Task.of(3)],
  Task.concurrent(2),
  Task.run
)

expect(results).toEqual([1,2,3])

# References

  • Task.all
  • Task.sequence

# Task.tryCatch

# Description

Try/catch a Task:

  • A Task that resolves will return an Ok.
  • A Task that rejects will return a Ko instead of throwing.
<A, E = unknown>(task: Task<A>) => Task<any>

# Example

const result = await pipe(
  Task.reject(4),
  Task.tryCatch
)

expect(result).toEqual(Result.ko(4))

# Task.thunk

# Description

Creates a Task from a thunk. If the thunk throws, fromIO will catch the error and create a Task that rejects.

<A>(fn: () => A | PromiseLike<A>) => Task<A>

# Task.from

# Description

Creates a Task from a PromiseLike.

<A>(promise: PromiseLike<A>) => Task<A>

# Task.struct

# Description

Merge a struct of Tasks into a single Task.

<A extends any>(obj: A, strategy: Task.Strategy<any>): Task.Struct<A>
(obj: any, strategy: Task.Strategy<any>): Task<any>
(strategy: Task.Strategy<any>): <A extends any>(obj: A) => Task.Struct<A>
(strategy: Task.Strategy<any>): (obj: any) => Task<any>

# Example

const relations = await pipe(
  {
    profiles: Task.thunk(() => findProfilesByUserId(userId)),
    permissions: Task.thunk(() => findPermissionsByUserId(userId)),
    posts: Task.thunk(() => findPostsByUserId(userId)),
    friends: Task.thunk(() => findFriendsByUserId(userId)),
  },
  Task.struct(Task.concurrent(2)),
  Task.run
)

# References

  • Prom.struct

# Task.timeout

# Description

Timeout a task after the given amount of milliseconds.

<A>(ms: number, fn: () => A | PromiseLike<A>) => (task: Task<A>) => Task<A>

# Example

const original = pipe(
  Task.sleep(10000),
  Task.map(() => "Hello!")
)

const withTimeout = await pipe(
  original,
  Task.timeout(5000, Task.reject(Err.of('Timeout!'))),
  Task.tryCatch,
  Task.run
)

expect(pipe(withTimeout, Result.isKo)).toBe(true)

# Task.taskify

# Description

Transforms a function into a function returning a Task.

<Args extends Array<any>, R>(fn: (...args: Args) => R | PromiseLike<R>) => (...args: Args) => Task<R>

# Example

const operation = async (a: number, b: number) => {
  ...
}
const lazyOperation = Task.taskify(operation)

// Create task - Operation not yet executed
const task = lazyOperation(10, 20)

// Execute task
const result = await task

# Task.retry

# Description

Retry a task with the given retry strategy

(strategy: Task.RetryStrategy) => <A>(task: Task<A>) => Task<A>

# Example

// Get random number and retry until we get a positiv number, with a maximum of 10 tries.
const positiveNumber = await pipe(
  Task.thunk(() => Math.random()),
  Task.chain((nb) =>
    nb > 0
      ? Task.resolve(nb)
      : Task.reject(
          Err.of('Number is negative', {
            code: 'negative_number',
            value: nb
          })
        )
  ),
  Task.retry((err, attempt) => {
    if (attempt > 10) {
      return Task.reject(err)
    }
    if (err.code === 'negative_number') {
      return Task.sleep(1000)
    }
    return Task.reject(err)
  })
)

# Task.retryBy

# Description

Retry a task with the given retry options. This function is based on the Task.retry function.

(options: Task.RetryOptions) => <A>(task: Task<A>) => Task<A>

# Example

// Get random number and retry until we get a positiv number, with a maximum of 10 tries.
const positiveNumber = await pipe(
  Task.thunk(() => Math.random()),
  Task.chain((nb) =>
    nb > 0
      ? Task.resolve(nb)
      : Task.reject(
          Err.of('Number is negative', {
            code: 'negative_number',
            value: nb
          })
        )
  ),
  Task.retryBy({
    attemps: 10,
    delay: 1000,
    when: err => err.code === 'negative_number'
  })
)