mrkeyoor.com_
Wed 23 Sept 00:36 UTC
npmUtilsupdated 22 Sept 2026

qjobs review

qjobs 1.2.0 is a single-process callback queue for Node. Each queued item stores a function and a mutable arguments value; the function receives those arguments plus `next`, which releases one concurrency slot. The queue can pause new launches, discard waiting work, accept additions while running, emit lifecycle events, and expose counters. Its optional interval delays the next batch after the concurrency ceiling is reached. It has no persistence, Promise awareness, retry policy, worker pool, or multi-process coordination.

Verdict

Our qjobs 1.2.0 install took 0.7 seconds and left a single 1 MB package with no audit findings, but one missed `next()` can stall the queue forever. Keep it in stable callback code; choose a Promise-aware queue for new work.

We installed it

Lab card: what happened when we installed qjobsScreenshot of qjobs documentation
Install✓ · 0.7s1 package on disk · 1 MB
ImportESM import works · require() works · CommonJS package
Browsern/acould not be bundled for the browser (Node-only code, most likely)
Typesno TypeScript types found
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does qjobs install cleanly?

Yes. In a fresh container with an empty cache, npm install qjobs finished in 0.7s, leaving 1 package and 1 MB on disk. npm audit reported no known vulnerabilities.

Can qjobs run in a browser?

Not directly: esbuild could not bundle it for the browser in our run, which normally means it depends on Node built-ins. Use it on the server, or find a browser-targeted alternative.

Does qjobs work with both ESM and CommonJS?

Yes. Both import 'qjobs' and require('qjobs') worked in Node 22 in our run. The package is published as CommonJS.

Does qjobs include TypeScript types?

No type declarations were found in our install, so TypeScript users need their own declarations.

qjobs or p-queue: which should you use?

p-queue: Use it for Promise tasks, interval caps, priorities, timeouts, and idle waiting. Our qjobs 1.2.0 install took 0.7 seconds and left a single 1 MB package with no audit findings, but one missed next() can stall the queue forever.

When should you not use qjobs?

Jobs are async functions: qjobs ignores returned Promises and waits only for its callback

API stability3/5The constructor and its add, run, pause, abort, stats, concurrency, interval, and event APIs have stayed fixed at 1.2.0 since 2018. Existing code is unlikely to face an upgrade surprise. The contract itself is fragile: args are mutated, next must run exactly once, Promise returns are ignored, and errors lack a callback slot. A 3 recognizes frozen compatibility without treating those underspecified behaviors as a dependable modern API.
Docs2/5The README names the concurrency, dynamic-addition, pause, batch-delay, event, and statistics features and gives one full callback example. It does not define every method or event payload, say that args are mutated with `_jobId`, explain the exact abort cycle, or cover thrown errors, rejected Promises, missing next calls, duplicate next calls, and post-end additions. Source inspection is required for behavior that can stop or corrupt a queue.
Maintenance1/5npm still serves 1.2.0, and GitHub reports the last push on 2018-02-19. The repository is open and npm does not label the package deprecated, yet no newer typing, packaging, error handling, or async-function work has arrived. GitHub reports 3 open issues and pull requests combined. Users adopting it now should assume responsibility for Node compatibility and behavioral fixes.
Ecosystem2/5npm counted 3,174,384 downloads last week, while the repository has 19 stars and the package contains no integrations beyond Node's EventEmitter. With 0 dependencies it is easy to embed, but there are no persistence drivers, retry plugins, TypeScript declarations, framework adapters, or worker backends. The large install count likely includes transitive legacy use and does not signal an active extension community.

Use it if

  • You maintain callback-era code already wired to qjobs events and the mandatory `next()` convention
  • Pending work may disappear on process exit because the queue is strictly in memory
  • A concurrency cap, pause switch, progress counters, and dynamic additions are enough
  • Each job can own its errors and call `next()` exactly once on every completion path
Skip it if

Setup reality

We installed qjobs 1.2.0 in our sandbox in 0.7 seconds. It left 1 package and 1 MB on disk; npm audit found 0 known vulnerabilities. The package is 64 KB unpacked, has 0 direct dependencies and 0 peer dependencies, uses MIT, and declares Node 0.9 or newer. There are no credentials, native builds, config files, or external services.

The package is CommonJS and has no exports map. require() and ESM import worked in Node 22, while no TypeScript declarations were present. Our esbuild browser build failed, which fits a module based on Node's EventEmitter. Construct the queue, register listeners before run(), and pass an object or array as every job's args because the source writes _jobId onto it.

Every job must invoke next() exactly once. Omitting it permanently consumes a concurrency slot; calling twice drives counters out of sync. A thrown error escapes from the timer callback, and a rejected Promise is invisible unless your adapter catches it and calls next(). pause(true) stops new starts and emits a pause duration once per second, though jobs already running continue.

abort() sets a flag, then pending entries are cleared on a later run cycle; active jobs still finish. The interval option waits after a full batch drains and emits the misspelled continu event when work resumes. It is not a per-job delay. Jobs added after an end event do not automatically restart, so call run() again. stats() property names begin with _ and can calculate an invalid percentage before jobs exist.

Patterns

Set a four-job concurrency ceiling create-queue

const QJobs = require('qjobs');
const queue = new QJobs({ maxConcurrency: 4 });

The constructor falls back to a concurrency of 10 when the supplied value is falsy.

Add one callback job add-job

queue.add(function (args, next) {
  fetchRecord(args.id, (error, record) => {
    args.error = error || null;
    args.record = record;
    next();
  });
}, { id: 42 });

qjobs adds `_jobId` to the args object. Call `next()` exactly once on both success and failure paths.

Listen before starting work start-queue

queue.on('start', () => console.log('started'));
queue.on('end', () => console.log('drained'));
queue.run();

`start` is emitted synchronously inside `run()`, so listeners registered afterward miss it.

Log individual starts and finishes observe-jobs

queue.on('jobStart', args => console.log('start', args._jobId));
queue.on('jobEnd', args => console.log('end', args._jobId));

Both events receive the same mutable args value supplied to `add()`.

Bridge a Promise task to next adapt-promise

queue.add(function (args, next) {
  processItem(args.item).then(
    value => { args.value = value; next(); },
    error => { args.error = error; next(); }
  );
}, { item });

Returning the Promise has no effect on qjobs. Both settlement branches must release the slot.

Pause pending launches for five seconds pause-queue

queue.pause(true);
setTimeout(() => queue.pause(false), 5000);

Running jobs continue. While paused, a timer emits the `pause` event every 1 second.

Poll queue progress read-stats

const state = queue.stats();
console.log(state._status, state._progress, state._jobsRunning);

Every field name begins with `_`, and `_progress` can be NaN when no jobs have been added.

Wait between full concurrency batches delay-batches

const queue = new QJobs({ maxConcurrency: 5, interval: 1000 });
queue.on('sleep', () => console.log('waiting'));
queue.on('continu', () => console.log('resuming'));

The event name is `continu`. A 1000 ms interval does not impose a five-jobs-per-second quota.

Discard work that has not started abort-pending

queue.abort();

Active jobs are not cancelled and still have to call `next()`. Pending entries clear during a subsequent run cycle.

Run newly added work after a drain restart-after-end

function enqueueLater(item) {
  queue.add(worker, { item });
  queue.run();
}

A completed queue does not automatically start jobs added later. Call `run()` after the addition.

Alternatives

PackageRegistryPick it when
p-queuenpmUse it for Promise tasks, interval caps, priorities, timeouts, and idle waiting.
fastqnpmUse it for a focused in-memory worker queue with callback and Promise forms.
asyncnpmUse it when a concurrency queue is one part of a larger callback control-flow toolkit.

More utils guides

lru-cache · ajv · type-fest · p-limit · find-up · js-yaml · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.