use-tasker
v0.0.5
Published
A hooks based asynchronous task runner for react
Downloads
21
Maintainers
Readme
useTasker
A React Hook to schedule asynchronous things.
Install
yarn add use-tasker
Usage
Import the repo and try out the example in CodeSandbox:
https://codesandbox.io/s/github/boopathi/use-tasker
Example:
import { useTasker, seq, concurrent, task } from "use-tasker";
const sleep = (t) => new Promise((r) => setTimeout(r, t));
function Pipeline() {
const { state, start } = useTasker(
seq(
"pipeline",
task("title 1", async () => {
await sleep(2000);
}),
concurrent(
"deploy",
task("deploy pods", async () => {
await sleep(3000);
}),
task("deploy service", async () => {
await sleep(3000);
}),
task("deploy ingress", async () => {
await sleep(3000);
})
)
)
);
return (
<div>
<pre>
<code>{JSON.stringify(state, null, 2)}</code>
</pre>
</div>
);
}
API
useTasker
const { state, start } = useTasker(taskList, context);
- taskList:
TaskList
- TheTaskList
returned byseq
orconcurrent
functions described below - context:
any
- The parameter passed to alltaskFn
functions. You can use this as an internal state to pass values from one task to another. - returns
{ state, start }
- state:
TaskerState
- A nested structure that is updated as the tasks complete. This is the store of a reducer. More details understate
topic. - start:
() => void
- The function to start running the tasks. Add this as the click event handler of a button or a similar other thing.
state
The state
returned by the useTasker
hook gives the current state of the execution pipeline. It's a nested recursive structure that is derived from the input structure of the taskList
.
The type definition of the state
can be imported for TS projects and is defined as -
type TaskRunStatus = "NOT_STARTED" | "LOADING" | "SUCCESS" | "ERROR";
interface TaskerState {
title: string;
status: TaskRunStatus;
tasks?: TaskerState[];
error?: any;
}
The state updates as and when the execution proceeds and triggers a re-render of the component that uses useTasker
. You can create two mututally recursive components to render this recursive state. For example, the following code will render the recursive state as a nested ul
, li
list.
import { TaskerState } from "use-tasker";
function ListItem({ data }: { data: TaskerState }) {
return (
<li>
{data.status}: {data.title} ({data.error ? data.error.message : null})
{data.tasks ? <List tasks={data.tasks} /> : null}
</li>
);
}
function List({ tasks }: { tasks: TaskerState["tasks"] }) {
return (
<ul>
{tasks.map((it) => (
<ListItem data={it} key={it.title + it.status} />
))}
</ul>
);
}
task
import { task } from "use-tasker";
The task specifier. A task can retur any value including promises. If a task returns, it means it is successfully completed. If the task throws an error or returns a rejected promise, the task failed.
task(name, taskFn);
- name:
string
- name of the task - taskFn:
(context) => any | Promise<any>
- the task function- context:
any
- the context passed touseTasker
- context:
- returns
Task
seq
import { seq } from "use-tasker";
A sequence TaskList
specifier. Members of the sequence are executed sequentially. An Error in one of the steps will stop the execution of the sequence.
seq(name, ...taskLike);
- name:
string
- name for the sequence - taskLike:
Task | TaskList
- aTask
returned by thetask
function or another seq or concurrentTaskList
returned byseq
orconcurrent
functions - returns
TaskList
concurrent
import { concurrent } from "use-tasker";
A concurrent TaskList
specifier. Members are executed concurrently using Promise.allSettled
. An error in one or more of the members will be collected and thrown as a ConcurrentTasksError
- more details about this error is described below in Error Handling.
concurrent(name, ...taskLike);
- name:
string
- name for the concurrent tasks - taskLike:
Task | TaskList
- aTask
returned by thetask
function or another seq or concurrentTaskList
returned byseq
orconcurrent
functions - returns
TaskList
Error handling
task
: Tasks can throw an error or return a rejectedPromise
. Depending on whether it is used as a member ofseq
orconcurrent
, the error is handled accordingly.task("title1", () => { throw new Error("title1"); }); task("title2", () => { return Promise.reject(new Error("title2")); });
seq
: Errors thrown or rejected promises inseq
stop the current pipeline.concurrent
: Errors thrown or rejected promises inconcurrent
are collected as it is collected byPromise.allSettled
. They are then wrapped using a custom error class -ConcurrentTasksError
. For example,import { ConcurrentTasksError } from "use-tasker"; function ErrorComponent(state: TaskerState) { if (state.error) { if (state.error instanceof ConcurrentTasksError) { return <div>{state.error.errors.map((err) => err.message)}</div>; } } return null; }
Related
This project is inspired by Listr, which is a terminal task list.
License
MIT