henrygd/queue

repository·main·Indexed 41 days ago

https://github.com/henrygd/queue

A tiny, high-performance async queue library with concurrency control and optional time-based rate limiting. Version 1.2.0 provides a smaller and faster alternative to p-limit or fastq, featuring methods like add(), all(), and done() to manage asynchronous task execution.

Tokens
2K
Snippets
6
Records
8
Agent score
50%

What's inside henrygd/queue

  1. Basic usage of @henrygd/queue

    main

    To use the queue, create a new instance using newQueue(concurrency) and add async functions or promise-returning functions using the add method. The add method returns a promise that resolves with the same value as the supplied function. You can use queue.done() to wait for all tasks in the queue to complete.

    import { newQueue } from '@henrygd/queue'
    
    // create a new queue with a concurrency of 2
    const queue = newQueue(2)
    
    const pokemon = ['ditto', 'hitmonlee', 'pidgeot', 'poliwhirl', 'golem', 'charizard']
    
    for (const name of pokemon) {
        queue.add(async () => {
            const res = await fetch(`https://pokeapi.co/api/v2/pokemon/${name}`)
            const json = await res.json()
            console.log(`${json.name}: ${json.height * 10}cm | ${json.weight / 10}kg`)
        })
    }
    
    console.log('running')
    await queue.done()
    console.log('done')
  2. How to use AsyncLocalStorage with the queue

    main

    If your application requires support for Node.js AsyncLocalStorage, import the queue from the specific async-storage entry point instead of the default one:

    import { ... } from '@henrygd/queue/async-storage'

  3. Implement time-based rate limiting with @henrygd/queue/rl

    main

    For APIs with rate limits, use the @henrygd/queue/rl entry point. This allows you to limit both concurrency (simultaneous tasks) and the rate (tasks started within a specific time window).

    Use newQueue(concurrency, rate?, interval?) where:

    • concurrency: Maximum number of tasks running simultaneously.
    • rate: Maximum number of tasks that can start within the interval (optional).
    • interval: Time window in milliseconds for rate limiting (optional).
    import { newQueue } from '@henrygd/queue/rl'
    
    // max 10 concurrent requests, but only 3 can start per second
    const queue = newQueue(10, 3, 1000)
    
    const start = Date.now()
    
    for (let i = 1; i <= 10; i++) {
       queue.add(async () => console.log(`Task ${i} started at ${Date.now() - start}ms`))
    }
    
    await queue.done()
  4. Process multiple tasks with queue.all()

    main

    The queue.all method behaves similarly to Promise.all but applies the queue's concurrency control. It accepts an array containing either wrapper functions (that return promises) or existing promises. Note that existing promises start executing immediately upon creation; only the wrapper functions are subject to the queue's concurrency limits.

    import { newQueue } from "@henrygd/queue";
    
    const queue = newQueue(2);
    
    const tasks = ["ditto", "hitmonlee", "pidgeot", "poliwhirl"].map(
      (name) => async () => {
        const res = await fetch(`https://pokeapi.co/api/v2/pokemon/${name}`);
        return await res.json();
      }
    );
    
    // Process all tasks concurrently (limited by queue concurrency) and wait for all to complete
    const results = await queue.all(tasks);
    console.log(results);
  5. Queue Interface Reference

    main

    The following methods are available on a queue instance:

    • add<T>(promiseFunction: () => PromiseLike<T>): Promise<T>: Adds an async function or promise wrapper to the queue.
    • all<T>(promiseFunctions: Array<PromiseLike<T> | (() => PromiseLike<T>)>): Promise<T[]>: Adds promises or wrappers and resolves like Promise.all.
    • done(): Promise<void>: Returns a promise that resolves when the queue is empty.
    • clear(): void: Empties the queue. Pending promises are rejected, but active promises continue.
    • active(): number: Returns the number of promises currently running.
    • size(): number: Returns the total number of promises currently in the queue.
  6. Use the `Queue` interface

    main

    The Queue interface provides methods to manage asynchronous task execution with controlled concurrency.

    Methods:

    • add<T>(promiseFunction: () => PromiseLike<T>): Promise<T>: Adds an async function (a wrapper that returns a promise) to the queue. Returns a promise that resolves with the task's result.
    • all<T>(promiseFunctions: Array<PromiseLike<T> | (() => PromiseLike<T>)>): Promise<T[]>: Adds multiple promises or promise wrappers to the queue and resolves like Promise.all once all are completed.
    • done(): Promise<void>: Returns a promise that resolves when the queue is completely empty (no active or pending tasks).
    • clear(): void: Empties the queue. Note that active promises currently running are not cancelled, but pending promises are rejected with an Error('Queue cleared').
    • active(): number: Returns the number of promises currently running.
    • size(): number: Returns the total number of promises currently in the queue (both running and pending).
    import { newQueue } from '@henrygd/queue';
    
    const queue = newQueue(2);
    
    // Adding a single task
    queue.add(() => fetch('https://api.example.com/data'))
      .then(data => console.log(data));
    
    // Adding multiple tasks at once
    queue.all([
      () => Promise.resolve(1),
      () => Promise.resolve(2),
      () => Promise.resolve(3)
    ]).then(results => console.log(results)); // [1, 2, 3]
    
    // Waiting for everything to finish
    await queue.done();
  7. Clear the queue with `clear()`

    main

    Calling clear() will immediately reject all pending tasks in the queue with an Error('Queue cleared'). Any tasks that are already active (currently executing) will continue to run until they resolve or reject naturally. The size() of the queue will be updated to reflect only the remaining active tasks.

    const queue = newQueue(1);
    
    queue.add(() => new Promise(res => setTimeout(res, 1000))); // Active
    queue.add(() => Promise.resolve('pending')); // Pending
    
    queue.clear(); 
    // The first task continues running.
    // The second task rejects with Error('Queue cleared').
  8. Create a new queue with `newQueue()`

    main

    Use newQueue(concurrency) to initialize a new queue instance. The concurrency parameter defines the maximum number of operations allowed to run simultaneously. This is useful for controlling resource usage when executing many asynchronous tasks.

    import { newQueue } from '@henrygd/queue';
    
    const queue = newQueue(2); // Max 2 concurrent operations