A tiny but very useful JavaScript task orchestrator.
The one with reactivity - and React!
Task
management is hard.
Background tasks like fetching, syncing, and cache eviction are common in intelligent web applications. Yet managing them - with scheduling, failure handling, retries, and so on - can be a pain.
So make it easy.
Specify your tasks imperatively, ahead of time, and then configure their schedules, timeouts, and retry sequences - and let TinyTick take care of everything for you. Oh and it's only 2.6kB.
Create and start a Manager
object.
This is the main entry point for the TinyTick API.
import {createManager} from 'tinytick';
const manager = createManager().start();
Register a Task
.
A TinyTick task is simply an asynchronous function that can take an optional string argument (and a few other things, as you'll see later!). Simply register it with a string Id
.
const ping = async (url) => await fetch(url);
manager.setTask('ping', ping);
Schedule it to run.
By default, TinyTask schedules the task to start as soon as possible. And it will generate a unique Id
for each 'task run' so you can track its progress.
const taskRunId = manager.scheduleTaskRun(
'ping',
'https://example.com',
);
Keep up with what is going on.
The Manager
object exposes plenty of accessors to let you inspect the tasks you have registered and the state of the task runs you've scheduled.
console.log(manager.getTaskIds());
// -> ['ping']
console.log(manager.getTaskRunInfo(taskRunId));
// -> {taskId: 'ping', arg: 'https://example.com', ...}
TinyTick is reactive.
Subscribe to listeners that fire whenever critical things happen, like when a task starts, finishes, or fails. It uses the same API as TinyBase, so if you are familiar with listeners in that library, you'll feel right at home!
const listenerId1 = manager.addTaskRunRunningListener(
'ping',
null,
() => console.log('A ping started'),
);
const listenerId2 = manager.addTaskRunFailedListener(
'ping',
null,
() => console.log('A ping failed'),
);
// ...
manager.delListener(listenerId1);
manager.delListener(listenerId2);
Configure timeouts for your tasks.
Tasks (or individual task runs) can have a timeout set, and they will be aborted if they run over. Task
functions are passed an AbortSignal parameter so you can handle the timeout. You can pass this straight on to the fetch call, for example.
manager.setTask(
'ping',
async (url, signal) => await fetch(url, {signal}),
undefined,
{maxDuration: 100}, // milliseconds
);
Orchestrate retries.
If a task run fails (for taking too long, or throwing an exception), you can indicate that you want it to retry, and even configure a backoff strategy.
manager.setTask(
'ping',
async (url, signal) => await fetch(url, {signal}),
undefined, // we'll explain this argument in a moment!
{maxRetries: 3, retryDelay: '1000, 5000, 10000'},
);
Create configuration categories.
A Task
can be assigned a category, which can have its own configuration for duration, retries, and retry delays. But of course, individual properties can still be overridden per task or per task run.
manager.setCategory('network', {
maxDuration: 100,
maxRetries: 3,
retryDelay: '1000, 5000, 10000',
});
manager.setTask('ping', ping, 'network', {
maxRetries: 5,
});
Integrates with React.
The optional Provider
component and a set of hooks in the ui-react
module make it easy to integrate TinyTick into your React application so that you can start tasks or visualize their progress.
import {
Provider,
useCreateManager,
useScheduleTaskRunCallback,
useSetTask,
} from 'tinytick/ui-react';
const App = () => (
<Provider manager={useCreateManager(createManager)}>
<Panel />
</Provider>
);
const Panel = () => {
useSetTask(
'ping',
async () => await fetch('https://example.org'),
);
return <Button />;
};
const Button = () => {
const callback = useScheduleTaskRunCallback('ping');
return <button onClick={callback}>Ping</button>;
};
See some worked examples.
We are building up a set of Example Use Cases guides to show you how to use TinyTick in practice. If you're trying to access relational- or graph-like data over a network, for example, take a look at the Paginated And Nested Data guide for a start!
manager.scheduleTaskRun('fetchParents');
// -> 'Fetching https://api.org/parents?page=1'
// -> 'Storing parent A'
// -> 'Storing parent B'
// -> 'Fetching https://api.org/children?parentId=A&page=1'
// -> 'Fetching https://api.org/children?parentId=B&page=1'
// -> 'Fetching https://api.org/parents?page=2'
Tiny, tested, and documented.
If you chose to install TinyTick in your app, you'll only add a gzipped 2.6kB to your app. Life is easy when you have zero dependencies!
TinyBase has 100.0% test coverage, including the code throughout the documentation - even on this page. The guides, demos, and API examples are designed to make things as easy as possible.
Total | Tested | Coverage | |
---|---|---|---|
Lines | 405 | 405 | 100.0% |
Statements | 457 | 457 | 100.0% |
Functions | 174 | 174 | 100.0% |
Branches | 141 | 141 | 100.0% |
Tests | 165 | ||
Assertions | 590 |
Meet the family
TinyTick is part of a group of small libraries designed to help make rich client and local-first apps easier to build. Check out the others!
TinyBase
A reactive data store and sync engine.
TinyWidgets
A collection of tiny, reusable, UI components.
TinyTick
A tiny but very useful task orchestrator.