use-tasker v0.0.5
useTasker
A React Hook to schedule asynchronous things.
Install
yarn add use-taskerUsage
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- TheTaskListreturned byseqorconcurrentfunctions described below - context:
any- The parameter passed to alltaskFnfunctions. 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 understatetopic. - 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- aTaskreturned by thetaskfunction or another seq or concurrentTaskListreturned byseqorconcurrentfunctions - 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- aTaskreturned by thetaskfunction or another seq or concurrentTaskListreturned byseqorconcurrentfunctions - returns
TaskList
Error handling
task: Tasks can throw an error or return a rejectedPromise. Depending on whether it is used as a member ofseqorconcurrent, 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 inseqstop the current pipeline.concurrent: Errors thrown or rejected promises inconcurrentare 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