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

@react-dnd/asap review

@react-dnd/asap 5.0.2 is the small callback scheduler used inside the React DnD monorepo. `asap(fn)` queues work after the current synchronous call and drains callbacks in insertion order before yielding to rendering or I/O. Browser pages use MutationObserver when present; Node, workers, and older environments fall back to a zero-delay timeout backed by a 50 ms interval. Task errors are captured so the queue keeps moving, then rethrown on a later timer. Version 5.0.2 changes packaging only: compared with 5.0.1, it removes `.swcrc` and `tsconfig.json` from the tarball while leaving runtime files unchanged.

Verdict

@react-dnd/asap 5.0.2 installed as 1 dependency-free package and bundled to 0.8 KB gzipped in our sandbox, but it can drain an unbounded recursive queue before input, rendering, or I/O. Do not add it merely to defer work; keep it where React DnD or exact legacy ordering already requires it.

We installed it

Lab card: what happened when we installed @react-dnd/asapScreenshot of @react-dnd/asap documentation
Install✓ · 1.5s1 package on disk · 1 MB
ImportESM import works · require() works · ESM package
Browser0.8 KBgzipped (1.6 KB minified), bundled with esbuild
TypesTypeScript types bundled
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does @react-dnd/asap install cleanly?

Yes. In a fresh container with an empty cache, npm install @react-dnd/asap finished in 2 seconds, leaving 1 package and 1 MB on disk. npm audit reported no known vulnerabilities.

How much does @react-dnd/asap add to a browser bundle?

0.8 KB gzipped (1.6 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.

Does @react-dnd/asap work with both ESM and CommonJS?

Yes. Both import '@react-dnd/asap' and require('@react-dnd/asap') worked in Node 22 in our run. The package is published as ESM.

Does @react-dnd/asap include TypeScript types?

Yes, type declarations ship inside the package, so no @types install is needed.

@react-dnd/asap or queue-microtask: which should you use?

queue-microtask: Use it when you want the standard queueMicrotask call plus a fallback for runtimes that lack the global. @react-dnd/asap 5.0.2 installed as 1 dependency-free package and bundled to 0.8 KB gzipped in our sandbox, but it can drain an unbounded recursive queue before input, rendering, or I/O.

When should you not use @react-dnd/asap?

You only need a microtask in a current runtime. The built-in queueMicrotask() avoids another package and states the scheduling intent directly.

API stability4/5@react-dnd/asap 5.0.2 exposes 3 runtime values: `asap`, `AsapQueue`, and `TaskFactory`, plus Task types in its declarations. The FIFO flush, same-flush recursive enqueue, timer fallback, and delayed error rethrow follow the older ASAP implementation. A tarball comparison shows 5.0.2 changed only its version and removed 2 build-config files from publication. The API is unlikely to move, though that confidence comes from a package frozen since 2022 rather than a published compatibility policy.
Docs2/5The included README gives specific warnings about starvation, recursive scheduling, rendering, I/O, timer fallbacks, and delayed exception handling. It also documents the wrong package surface. The examples call `require('asap')`, describe `asap/raw`, discuss Node 0.10, and list browsers from the early 2010s. Version 5.0.2 is a named-export ESM package with bundled declarations and no raw subpath. The TypeScript declarations and source are therefore more reliable than the user-facing README.
Maintenance2/5npm published 5.0.2 on April 19, 2022. GitHub path history shows the final package-specific changes in January 2023, when the monorepo adjusted CommonJS build output and tooling, but no later @react-dnd/asap release carried those changes to npm. The containing React DnD repository is not archived and was pushed in July 2025. Its 475 open issues and pull requests cover the full drag-and-drop project, so they do not show active support for this scheduler.
Ecosystem3/5The npm endpoint counted 4,684,069 downloads from August 18 through 24, 2026, and the containing React DnD repository has 21,636 stars. Those are strong transitive-distribution signals, but the utility has no standalone plugin system, docs site, issue area, or integration layer. Current JavaScript already supplies `queueMicrotask()` and `requestAnimationFrame()`, while the published `scheduler` package covers priority-aware work. Most teams consume this queue because another package chose it.

Use it if

  • You maintain React DnD internals or another dependency whose ordering tests already assume this exact shared queue.
  • Callbacks added during a flush must run in that same flush before the scheduler yields.
  • A dependency needs the package's MutationObserver and timer fallback across its established environment matrix.
  • You are auditing a transitive install and need to understand why long recursive task chains can block input or I/O.
Skip it if

Setup reality

Our Node 22 sandbox installed @react-dnd/asap 5.0.2 in 1.5 seconds. The result was 1 package and 1 MB on disk; the package itself was 168 KB unpacked. npm audit found 0 known vulnerabilities. There are 0 direct dependencies and 0 peer dependencies. The MIT package is ESM, has no exports map, and bundles TypeScript declarations. Both require() and ESM import worked under Node 22 in our check. The full browser bundle measured 1.6 KB minified and 0.8 KB gzipped.

Use import {asap} from '@react-dnd/asap'; the bundled README's require('asap') example targets the original package, not this scoped build. Version 5.0.2 does not publish an asap/raw subpath. Its patch over 5.0.1 only removes 2 build configuration files from the npm tarball, so no scheduler behavior changed. There are no credentials, native builds, environment variables, or configuration files. The queue is module-global unless you construct the lower-level AsapQueue and TaskFactory classes yourself.

Scheduling priority is the catch. Once flushing starts, the loop runs until its queue is empty and includes callbacks enqueued by earlier callbacks. That preserves order but permits an infinite recursive chain to occupy one event-loop turn. The implementation periodically compacts after 1,024 completed tasks to limit retained array entries; this protects memory bookkeeping, not fairness. Use requestAnimationFrame() for visual work, a timer or scheduler for yielding chunks, and queueMicrotask() for ordinary current-runtime microtasks.

An uncaught callback error is handed to a pending-error list and thrown later through the timer fallback. A try/catch around the original asap() call cannot catch it because the callback runs after that stack has returned. Catch expected failures inside the callback or resolve/reject your own Promise. asap() offers no cancellation handle and no completion value. Tests that need to wait can wrap one scheduled callback in a Promise, while application code should avoid adopting this package unless exact inherited semantics matter.

Patterns

Run after the current stack defer-callback

import {asap} from '@react-dnd/asap';

console.log('before');
asap(() => console.log('queued'));
console.log('after');

The output order is `before`, `after`, `queued`; version 5.0.2 never invokes the callback inline.

Keep callback insertion order preserve-fifo-order

asap(() => console.log('one'));
asap(() => console.log('two'));
asap(() => console.log('three'));

One module-global queue runs these 3 callbacks in FIFO order during the same flush.

Append work from a running task enqueue-during-flush

asap(() => {
  console.log('outer');
  asap(() => console.log('nested'));
});
asap(() => console.log('sibling'));

The order is `outer`, `sibling`, `nested`; the nested callback joins the active flush instead of waiting for a later turn.

Make cache hits asynchronous avoid-mixed-callback-timing

function loadValue(key, done) {
  const cached = cache.get(key);
  if (cached !== undefined) {
    asap(() => done(null, cached));
    return;
  }
  readValue(key, done);
}

The cache-hit callback runs after `loadValue()` returns, matching an asynchronous miss path more closely.

Catch an expected callback failure handle-task-error

asap(() => {
  try {
    rebuildIndex();
  } catch (error) {
    reportFailure(error);
  }
});

Without the inner catch, version 5.0.2 queues the error and throws it on a later timer while continuing other tasks.

Give a test an awaitable boundary await-one-flush

function nextAsapTask() {
  return new Promise((resolve) => asap(resolve));
}

asap(() => updateModel());
await nextAsapTask();
expect(model.updated).toBe(true);

`asap()` returns `void`; the Promise wrapper waits for its own queued callback and does not capture errors from unrelated tasks.

Notify listeners after a state write notify-observers

function setState(next) {
  state = next;
  for (const listener of listeners) {
    asap(listener);
  }
}

Listeners run in iteration order, but the queue does not deduplicate 2 registrations of the same function or isolate a slow listener.

Isolate lower-level task ordering create-private-queue

import {AsapQueue, TaskFactory} from '@react-dnd/asap';

const queue = new AsapQueue();
const factory = new TaskFactory(queue.registerPendingError);

queue.enqueueTask(factory.create(() => {
  console.log('private queue');
}));

This bypasses the module-global `asap` queue and couples the caller to 2 lower-level classes that the README does not teach.

Let callers attach an event listener emit-after-subscription

import {EventEmitter} from 'node:events';

function createJob() {
  const job = new EventEmitter();
  asap(() => job.emit('ready'));
  return job;
}

createJob().on('ready', () => console.log('ready'));

The deferred emission occurs after `createJob()` returns, giving the caller time to attach the `ready` listener.

Prefer the platform for new deferral use-native-microtask

queueMicrotask(() => {
  notifySubscribers();
});

Current Node and browsers provide `queueMicrotask()` without a package; it is the simpler choice when ASAP-specific fallbacks are unnecessary.

Align visual work with paint schedule-visual-update

requestAnimationFrame(() => {
  drawSelectionOverlay();
});

Visual updates should use the rendering clock; an ASAP flush runs before rendering and can postpone a frame.

Break CPU work into timer chunks yield-long-work

function runChunk() {
  for (let i = 0; i < 100 && jobs.length; i += 1) {
    processJob(jobs.shift());
  }
  if (jobs.length) setTimeout(runChunk, 0);
}

setTimeout(runChunk, 0);

A timer yields between 100-job chunks; recursively calling `asap(runChunk)` would keep extending one high-priority flush.

Alternatives

PackageRegistryPick it when
queue-microtasknpmUse it when you want the standard queueMicrotask call plus a fallback for runtimes that lack the global.
schedulernpmUse it when work needs explicit priority levels and should cooperate with rendering rather than drain one FIFO immediately.
p-immediatenpmUse it when Promise-based deferral to an immediate task fits better than a callback-only queue.
asapnpmKeep it only in software that explicitly depends on the original package and its documented raw entry point.

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.