Aller au contenu principal

Composing Tasks

Chaining and combining tasks

API

Overview​

Tasks are designed to be composed together. Multiple operations can be chained without triggering execution until Task.run is called.

Transforming Values​

Task.map​

Transform the success value of a task:

import { Task } from '@w5s/task';

const numberTask = Task.resolve(42);
const stringTask = Task.map(numberTask, (n) => `The answer is ${n}`);

await Task.run(stringTask); // Result.Ok('The answer is 42')

Task.andThen​

Chain tasks that depend on previous results:

import { Task } from '@w5s/task';

const fetchUser = (id: string) => Task.create(async () => {
// ... fetch user
return Task.ok({ id, name: 'John' });
});

const fetchPosts = (userId: string) => Task.create(async () => {
// ... fetch posts
return Task.ok([{ title: 'Post 1' }]);
});

const userWithPosts = Task.andThen(
fetchUser('123'),
(user) => Task.map(
fetchPosts(user.id),
(posts) => ({ ...user, posts })
)
);

Task.andRun​

Chain tasks when and the previous value:

import { Task } from '@w5s/task';

const logStart = Task.create(() => Task.ok(console.log('Starting...')));
const mainTask = Task.create(() => Task.ok('result'));
const logEnd = Task.create(() => Task.ok(console.log('Done!')));

const workflow = Task.andRun(
Task.andRun(logStart, mainTask),
logEnd
);

Handling Errors​

Task.mapError​

Transform the error type of a task:

import { Task } from '@w5s/task';

const failingTask = Task.reject('string error');
const mappedError = Task.mapError(failingTask, (err) => new AnotherError(err));

Task.orElse​

Recover from errors:

import { Task } from '@w5s/task';

const failingTask = Task.reject(new NetworkError());
const recovered = Task.orElse(failingTask, (error) => {
console.log('Recovering from:', error);
return Task.resolve('fallback value');
});

Task.mapResult​

Transform both success and error at once:

import { Task } from '@w5s/task';

const task = Task.resolve(42);
const mapped = Task.mapResult(task, (result) => {
if (result.ok) {
return Result.Ok(result.value * 2);
}
return Result.Error(new WrappedError({ cause: result.error }));
});

Parallel Execution​

Task.all​

Run multiple tasks in parallel and collect all results:

import { Task } from '@w5s/task';

const tasks = [
Task.resolve(1),
Task.resolve(2),
Task.resolve(3),
];

const allResults = Task.all(tasks);
await Task.run(allResults); // Result.Ok([1, 2, 3])

// If any task fails, the whole operation fails
const withFailure = Task.all([
Task.resolve(1),
Task.reject('error'),
]);
await Task.run(withFailure); // Result.Error('error')

Task.any​

Get the first successful result:

import { Task } from '@w5s/task';

const first = Task.any([
Task.reject('error 1'),
Task.resolve('success'),
Task.reject('error 2'),
]);
await Task.run(first); // Result.Ok('success')

Task.allSettled​

Run all tasks and collect all results (success or failure):

import { Task } from '@w5s/task';

const settled = Task.allSettled([
Task.resolve(1),
Task.reject('error'),
Task.resolve(3),
]);
await Task.run(settled);
// Result.Ok([
// Result.Ok(1),
// Result.Error('error'),
// Result.Ok(3)
// ])

Utility Functions​

Task.ignore​

Discard the result value:

import { Task } from '@w5s/task';

const task = Task.resolve(42);
const ignored = Task.ignore(task);
await Task.run(ignored); // Result.Ok(undefined)