Skip to content

v10.0.0

Latest

Choose a tag to compare

@sindresorhus sindresorhus released this 12 Oct 18:04

Breaking

  • Require Node.js 22 300a52d
  • The queue is now an EventTarget instead of an EventEmitter 300a52d
    • Use .addEventListener() and .removeEventListener() instead of .on(), .once(), .off(), and the other EventEmitter methods.
    • The completed and error events have the result or the error in event.detail.
    • An error thrown in an event listener now exits the process. Node.js reports it as an uncaught exception.
  • .clear() now rejects the promises of the removed tasks with an AbortError instead of leaving them pending forever 64311ff
    • .clear() marks these promises as handled, but a promise made from them, like the one from .addAll() or queue.add(fn).then(…), now rejects, so handle it.
  • TimeoutError is now a DOMException named TimeoutError, like the error of AbortSignal.timeout() 300a52d
  • Remove the deprecated carryoverConcurrencyCount option: use carryoverIntervalCount 300a52d
  • Stricter validation of options 76a48ba d65d567
    • concurrency and intervalCap must be integers (or Infinity). A limit of 1.5 used to let 2 tasks run.
    • timeout and interval above 2147483647 ms (the maximum timer delay) are rejected. They used to fire after 1 ms.
    • .add() rejects with a TypeError when priority is NaN or not a number.
  • .start(), .clear(), and setting concurrency no longer emit empty and idle when the queue is already empty, so idle no longer fires on a queue that never had a task aeedf53
  • A custom queueClass must now implement remove() 300a52d

Improvements

  • Zero dependencies! 300a52d
  • A task now runs in the async context of its .add() call, so it sees the AsyncLocalStorage values of the caller, also when it starts later (Node.js 22.3 and later, Deno, and Bun) (#200) a5b7b54
    • Previously, a waiting task could see the values of whatever started it, like another request on a server.
  • A task with a timeout now gets a signal that aborts when the task times out, so you can pass it on to fetch() and similar to stop the work 300a52d
  • .onEmpty() and the empty event now fire when the last waiting task starts, not when the next task finishes 1cf5b82 cb25a0a
  • .addAll() now resolves with a typed tuple when the tasks return different types 36e83aa

Fixes

  • Fix many rate-limit bugs where intervalCap was exceeded at the end of a window, a waiting task got stuck, or isRateLimited was wrong 6af55a7 1cf5b82 6c08a4f ac89911 450356a
  • Fix slow add, run, and abort for many tasks that share a signal, and PriorityQueue keeping finished tasks in memory 95622a4 6fb8337
  • Fix many edge cases with events and priorities, like infinite recursion in an idle listener and events in the wrong order aeedf53 b33dd44 8792846 e79f970 d65d567 6f6028a
  • Fix undefined options and an invalid .timeout setter breaking the queue d6cf96b 48586f4
  • Fix many TypeScript types 36e83aa fa3c3e0

Migration guide

Events

-queue.on('completed', result => {
-	console.log(result);
-});
+queue.addEventListener('completed', event => {
+	console.log(event.detail);
+});

-queue.once('idle', () => {});
+queue.addEventListener('idle', () => {}, {once: true});

If the result or the error is undefined, event.detail is null, because CustomEvent works that way.

Other differences from EventEmitter:

  • If you add the same listener twice, it is called once.
  • A listener that is removed while an event is dispatched is not called for that event.

.clear()

The promises of the removed tasks now reject with an AbortError. If you await them, handle the rejection:

const results = await queue.addAll(tasks).catch(error => {
	if (error.name === 'AbortError') {
		return [];
	}

	throw error;
});

Timeouts and signals

error instanceof TimeoutError still works. You can also check error.name === 'TimeoutError', which also matches the error of AbortSignal.timeout().

Other changes:

  • The TimeoutError stack trace no longer shows where the task was added.
  • With timeout set, the task's signal is no longer linked to your signal after the task finishes.
  • Tasks with a signal or timeout settle 2 microtasks later than before.

v9.3.5...v10.0.0