Task Queues in TypeScript: Creation and Optimization of Processing

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Working with Task Queues in TypeScript: A Complete Guide

In modern web development and server applications, there is often a need to execute tasks asynchronously, in the background, or with concurrency limits. Task queues are a powerful pattern that allows organizing predictable and efficient execution of operations. In this article, we will thoroughly examine how to create and use task queues in TypeScript, from simple implementations to advanced techniques with priorities and error handling.



What is a task queue and why is it needed?

A task queue is a data structure operating on the FIFO (First In, First Out) principle. Tasks are added to the end of the queue and retrieved from the beginning. In the context of TypeScript and Node.js, queues are used for:

  • Concurrency limiting — controlling the number of simultaneously executing operations (e.g., API requests with limits).
  • Asynchronous processing — executing heavy tasks in the background without blocking the main thread.
  • Retries — automatic restart of failed tasks with a delay.
  • Order management — guaranteeing sequential execution of dependent operations.

TypeScript adds strict typing, making queue code more reliable and predictable.



Basic implementation of a task queue in TypeScript

Let's start by creating a simple but fully typed queue. It will accept tasks (functions returning a Promise) and execute them sequentially.

// Define the task typetype Task<T> = () => Promise<T>;

// Queue classclass TaskQueue<T> { private queue: Task<T>[] = []; private isProcessing: boolean = false;

// Add task to queue enqueue(task: Task<T>): void { this.queue.push(task); this.process(); }

// Start processing private async process(): Promise<void> { if (this.isProcessing) return; this.isProcessing = true;

while (this.queue.length > 0) { const task = this.queue.shift()!; try { const result = await task(); console.log('Task completed:', result); } catch (error) { console.error('Task execution error:', error); } }

this.isProcessing = false; }}

// Usage exampleconst queue = new TaskQueue<string>();queue.enqueue(async () => { await new Promise(resolve => setTimeout(resolve, 1000)); return 'First task';});queue.enqueue(async () => 'Second task (fast)');

This implementation guarantees sequential execution and automatic start of processing when a new task is added. However, it does not support parallelism or priorities.



Queue with concurrency limit

Often it is necessary to execute multiple tasks simultaneously but not exceed a certain limit (e.g., 5 parallel requests). Let's add a concurrency parameter.

class ParallelTaskQueue<T> {    private queue: Task<T>[] = [];    private activeCount: number = 0;    private readonly concurrency: number;

constructor(concurrency: number = 3) { this.concurrency = concurrency; }

enqueue(task: Task<T>): void { this.queue.push(task); this.processNext(); }

private processNext(): void { while (this.activeCount < this.concurrency && this.queue.length > 0) { const task = this.queue.shift()!; this.activeCount++; task() .then(result => console.log('Success:', result)) .catch(error => console.error('Error:', error)) .finally(

Blogs

Book Recommendations