All questions

Priority Request Scheduler

Premium

Priority Request Scheduler

A priority request scheduler runs at most concurrency async tasks at once and, whenever a slot frees, starts the highest-priority task waiting in line — not the one that has waited longest. It is what keeps a user-blocking fetch ahead of background prefetches when the browser or your API can only handle so many requests in parallel: you cap how many run together, and you make sure the critical one goes next.

Implement priorityRequestScheduler({ concurrency }). It returns a scheduler object. Calling schedule(task, priority) enqueues an async (or sync) task function and returns a promise that settles with task()'s result or rejection. A larger priority number is more urgent, and the default is 0. At most concurrency tasks run at once; the rest wait. When a running task settles, the freed slot goes to the highest-priority waiter, with ties broken in the order tasks were scheduled (FIFO). A task that rejects frees its slot like any other and never affects the others.

Signature

type Task<T> = () => Promise<T> | T;

interface Scheduler {
  // Enqueue a task; settles with its result or rejection. Higher priority runs first.
  schedule<T>(task: Task<T>, priority?: number): Promise<T>;
  readonly size: number; // tasks waiting for a slot
  readonly pending: number; // tasks running right now
}

function priorityRequestScheduler(options: { concurrency?: number }): Scheduler;

Examples

const scheduler = priorityRequestScheduler({ concurrency: 1 });

// One lane, and it is busy running `render`.
scheduler.schedule(render, 0);

// Three more arrive while the lane is full.
scheduler.schedule(prefetchA, 0); // background
scheduler.schedule(loadProfile, 10); // critical — higher priority
scheduler.schedule(prefetchB, 0); // background

// When `render` finishes, the freed lane goes to loadProfile (priority 10),
// NOT prefetchA — even though prefetchA was queued first.
const scheduler = priorityRequestScheduler({ concurrency: 2 });

const body = await scheduler.schedule(async () => {
  const res = await fetch('/api/user');
  return res.json();
});

// A task that rejects settles only its own promise; the slot is freed and the
// next waiter runs. Other tasks are untouched.
scheduler.schedule(async () => {
  throw new Error('offline');
}).catch(() => {});

Notes

  • Higher runs firstpriority is a number and a larger value is more urgent. When omitted it defaults to 0.
  • Decided when a slot frees — the highest-priority waiter is picked at dequeue time, so a task scheduled later can jump ahead of ones already waiting.
  • No preemption — a task that has already started is never interrupted; priority only orders the waiting line.
  • Ties are FIFO — waiters with equal priority run in the order they were scheduled.
  • A rejection frees its slot — a failing task settles its own promise and the scheduler keeps going for everyone else.
  • Assume concurrency is a positive integer — you do not need to validate it, change it later, or support cancellation.

FAQ

Which direction does priority go?
A larger number is more urgent. schedule(task, 10) outranks schedule(task, 1), and the default priority is 0. Whenever a slot frees, the highest-priority waiter is the one started next.
Does a high-priority task interrupt one that is already running?
No. Priority only orders the waiting line. A task that has already started runs to completion; the high-priority task takes the next slot that frees. Interrupting an in-flight request means cancelling it, which is out of scope here.
How are ties between equal priorities broken?
By insertion order (FIFO). Each task is stamped with a monotonic sequence number when scheduled, and equal priorities are ordered by that number, so the task scheduled earlier runs first.

Unlock the solution & editor

  • Runnable editor + tests

    Solve in the browser with instant Jest feedback.

  • Detailed solutions

    Walkthroughs, edge cases, and complexity notes.

  • Multi-framework variants

    React, Vue, Vanilla, Angular — same question, different stacks.

Upgrade to Premium