# @scratch/task-herder

> An asynchronous task runner with configurable rate limiting / throttling and concurrency control.

Latest version **15.1.1** (published 2026-09-02) · AGPL-3.0-only license · 0 weekly downloads

## Install

```sh
npm install @scratch/task-herder
pnpm add @scratch/task-herder
yarn add @scratch/task-herder
bun add @scratch/task-herder
```

## Health

**Score 60/100 (C)** — status: active.

Positive: esm support; no vulnerabilities; recently updated; high maintenance score.

Warnings: low downloads; no types.

## Facts

| | |
|---|---|
| Version | 15.1.1 |
| Published | 2026-09-02 |
| First published | 2025-12-15 |
| Weekly downloads | 0 |
| License | AGPL-3.0-only |
| TypeScript types | none |
| Module format | ESM + CommonJS |
| Dependencies | 0 |
| Unpacked size | 54.5 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Author | Scratch Foundation |
| Maintainers | cwillisf.scratch |
| Keywords | rate limit, throttle, task, queue, concurrency, concurrent |

## Links

- npm: https://www.npmjs.com/package/@scratch/task-herder
- npm.io page: https://npm.io/package/@scratch/task-herder

## Alternatives

- [cron](https://npm.io/package/cron.md) — 4.9M weekly downloads
- [@vercel/queue](https://npm.io/package/@vercel/queue.md) — 731.6K weekly downloads
- [create-sonicjs](https://npm.io/package/create-sonicjs.md) — 1.6K weekly downloads
- [@exellix/jobs-api](https://npm.io/package/@exellix/jobs-api.md) — 941 weekly downloads
- [@forwardimpact/libskill](https://npm.io/package/@forwardimpact/libskill.md) — 575 weekly downloads

## Recent versions

- 15.1.1 (latest) — 2026-09-02
- 15.0.2-revert-react-context-menu-static-init.1 (revert-react-context-menu-static-init) — 2026-08-07
- 15.0.0-svg-sandboxing.1 (svg-sandboxing) — 2026-07-24
- 14.2.0-accessibility-fixes-and-costume-updates.1 (accessibility-fixes-and-costume-updates) — 2026-07-03
- 14.2.0-2026-07-02.1 (2026-07-02) — 2026-07-02
- 13.8.0-UEPR-297-accessibility-improvements.1 (UEPR-297-accessibility-improvements) — 2026-05-26
- 13.8.0-test-new-release-workflow.1 (release/test-new-release-workflow) — 2026-05-19
- 14.0.0-accessibility-improvements.4 (release/UEPR-297-accessibility-improvements) — 2026-05-13
- 13.7.4-svg (hotfix/scratch-paint-svg-sanitization) — 2026-05-05
- 13.7.3 (hotfix/scratch-blocks-release-triage) — 2026-04-30
- 12.7.1 (hotfix/more-svg-sanitization) — 2026-04-07
- 13.4.0-test.7 (beta) — 2026-03-26
- 13.3.0-build-media-scripts (uepr-530-separate-media-libraries-build-script-in-its-own-package) — 2026-03-19
- 13.2.0-configurable-backpack (backpack-interface) — 2026-03-12
- 13.0.0-spork.1 (spork) — 2026-03-03
- … 57 more at https://npm.io/package/@scratch/task-herder/versions

## README

# Task Herder

Task Herder helps you herd your asynchronous ~~cats~~ ~~kats~~ tasks. Its main job is to provide a way to queue up
tasks and run them with configurable throttling and concurrency limits.

## Concepts

The queue operates according to the Token Bucket algorithm. The general idea is that you have a "bucket" that can hold
a certain number of "tokens". Each task "costs" a certain number of tokens (default: 1) to run. Tokens are added to
the bucket at a certain rate (the "refill rate") up to the maximum capacity of the bucket.

When a task is added to the queue, if there are enough tokens in the bucket, the task will "spend" the required number
of tokens and be eligible to run. If there are not enough tokens, the task will wait in the queue until enough tokens
are available. Tasks are run in the order they were added to the queue (FIFO), so keep in mind that an expensive task
may delay cheaper tasks behind it until it can run.

Eligible tasks are run immediately, up to the maximum concurrency limit. This is useful if you need to limit concurrent
resource usage with tasks that take an unpredictable amount of time. For example, you may want to make no more than 5
concurrent connections to a particular service at a time. If the concurrency limit is reached, eligible tasks will wait
their turn. This stage of processing is also FIFO.

Once a task makes it through both the token and concurrency checks, it is executed. The Promise generated by the Task
Queue will then resolve or reject based on the outcome of the task.

## Usage

```javascript
import TaskQueue from 'task-herder'

const queue = new TaskQueue({
  // The maximum number of tokens in the bucket controls the burst limit
  burstLimit: 10,

  // Rate at which tokens are added to the bucket (tokens per second) controls the sustained rate
  sustainRate: 5,

  // Initial number of tokens in the bucket
  // Default: burstLimit (i.e., start with a full bucket)
  startingTokens: 5,

  // Reject a task if it would cause the total queue cost to exceed this limit
  // Default: no limit
  queueCostLimit: 50,

  // Number of tasks that can be processed concurrently
  // Default: 1 (run tasks serially)
  concurrency: 3,
})

// Using `await` syntax
try {
  const response = await queue.do(
    // Your async task here
    () => fetch('https://example.com/data'),
    {
      // Optional cost of this task (default: 1)
      cost: 2,
    },
  )
  // Handle successful response
} catch (error) {
  // Handle error
}

// Using Promise syntax
queue
  .do(
    // Your async task here
    () => fetch('https://example.com/data'),
    {
      // Optional cost of this task (default: 1)
      cost: 2,
    },
  )
  .then((response) => {
    // Handle successful response
  })
  .catch((error) => {
    // Handle error
  })
```

You can also supply an `AbortSignal` to cancel a task before it starts:

```javascript
const controller = new AbortController()
queue
  .do(
    () =>
      fetch('https://example.com/data', {
        // Optionally, provide the same signal (or a different one) so it can interrupt the fetch request
        signal: controller.signal,
      }),
    {
      // Provide the signal to the task queue so it can cancel the task before it starts
      signal: controller.signal,
    },
  )
  .catch((error) => {
    if (error.name === 'AbortError') {
      // Handle task cancellation
    } else {
      // Handle other errors
    }
  })
```

Or, you can remove one task or all tasks from the queue before they start:

```javascript
const taskPromise = queue.do(() => fetch('https://example.com/data'))
queue.cancel(taskPromise, optionalReason) // Remove a specific task from the queue
queue.cancelAll(optionalReason) // Remove all tasks from the queue
```

## Donate

We provide [Scratch](https://scratch.mit.edu) free of charge, and want to keep it that way! Please consider making a
[donation](https://secure.donationpay.org/scratchfoundation/) to support our continued engineering, design, community,
and resource development efforts. Donations of any size are appreciated. Thank you!

---
_Source: https://npm.io/package/@scratch/task-herder · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
