npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

seq-prom

v3.0.2

Published

A small library to allow for sequential operations on an array using Promises

Readme

SeqProm

Type-safe promise processing for arrays in TypeScript/JavaScript

Installation

npm install seq-prom --save
pnpm add seq-prom

Options

| Option | Type | Description | Default | Required? | |-----------|----------|--------------------------------------------------------------------------------------------------|---------|-----------| | list | Array | List of items to iterate through | | Yes | | cb | Function | Type-safe callback function for processing each item | | Yes | | size | Integer | Size of the batch or pool (number of concurrent operations) | 1 | No | | errorCB | Function | Called when there is an error, with the item and reason for error | | No | | finalCB | Function | Called when all is done. Passes in a list of errors, and any items, passed to the resolve method | | No | | useBatch | Boolean | Process items in batches of size size | false | No | | autoStart | Boolean | Instead of having to call .start() will do this for you | false | No |

Core Concepts

Processing Modes

SeqProm supports two processing modes for handling collections of items:

Pool Mode (Default)

In pool mode, SeqProm creates a "pool" of concurrent promises, processing items as previous ones complete. This is ideal for handling a large number of items with controlled concurrency.

  • Set poolSize to control how many items process concurrently
  • Items start processing immediately up to the pool size limit
  • As each item completes, the next waiting item begins processing
  • Order of completion depends on how long each item takes to process

Batch Mode

In batch mode, SeqProm processes items in discrete batches of a specific size. Each batch completes fully before the next batch begins.

  • Set useBatch: true and size to define batch size
  • Items are processed in sequential batches
  • All items in a batch are processed concurrently
  • The next batch only starts after the current batch fully completes
  • Useful for operations that should happen in specific groups

Usage Examples

Basic Usage

Using Direct Return Values (Simplest)

import SeqProm from "seq-prom";

// Simply return a value from the callback
SeqProm({
  list: [1, 2, 3],
  autoStart: true,
  cb(item) {
    console.log(`Processing ${item}`);
    return `Result: ${item * 2}`;
  },
  finalCB(errors, responses) {
    console.log("All done!", responses);
  }
});

Using Async/Await

import SeqProm from "seq-prom";

// Asynchronous callback with async/await
SeqProm({
  list: [1, 2, 3],
  autoStart: true,
  cb: async (item) => {
    console.log(`Processing ${item}`);
    
    // Simulate async operation
    await new Promise(resolve => setTimeout(resolve, 100));
    
    return `Processed ${item}`;
  },
  finalCB(errors, responses) {
    console.log("All results:", responses.map(r => r.result));
  }
});

Using Resolve/Reject Functions

import SeqProm from "seq-prom";

// Using explicit resolve/reject callbacks
// Type parameters are needed for this style
let seqProm = SeqProm<number, void>({
  list: [1, 2, 3],
  cb(item, {resolve, reject}) {
    console.log(`Item [${item}] called!`);
    setTimeout(function () {
      if (item === 3) {
        return reject("Not sure about this!");
      } else {
        return resolve();
      }
    }, item * 1000);
  },
  errorCB(item, reason) {
    console.error(`Item [${item}] failed with error: ${reason}`);
  },
  finalCB() {
    console.log("All done!");
  }
});

seqProm.start();

Processing Mode Examples

Pool Mode

import SeqProm from "seq-prom";

// Processing with a pool of 2 concurrent operations using async/await
SeqProm({
  list: [1, 2, 3, 4],
  autoStart: true,
  size: 2,  // 2 concurrent operations at a time
  cb: async (item) => {
    console.log(`Processing item ${item}`);
    
    // Simulate async operation
    await new Promise(resolve => setTimeout(resolve, item * 200));
    
    // If item is 3, throw an error (will be caught internally)
    if (item === 3) {
      throw new Error("Processing error");
    }
    
    return `Result: ${item * 10}`;
  },
  errorCB(item, reason) {
    console.error(`Item [${item}] failed with error: ${reason}`);
  },
  finalCB(errors, responses) {
    console.log("All items processed!");
    console.log(`Successful: ${responses.length}, Failed: ${errors.length}`);
    
    // Print successful results
    responses.forEach(res => {
      console.log(`Item ${res.item} → ${res.result}`);
    });
  }
});

// Use the promise for further actions
const [errors, responses] = await SeqProm({
  list: [5, 6, 7],
  size: 2,
  cb: async (num) => {
    await new Promise(resolve => setTimeout(resolve, 100));
    return num * 2;
  }
}).start().promise;

console.log("All processing complete:", responses.map(r => r.result));

Batch Mode

import SeqProm from "seq-prom";

// Processing in batches using async/await
SeqProm({
  list: [1, 2, 3, 4, 5, 6],
  autoStart: true,
  useBatch: true,  // Enable batch processing mode
  size: 2,         // Process items in batches of 2
  
  // Using async callback
  cb: async (item) => {
    console.log(`Batch processing item ${item}`);
    
    try {
      // Simulate database operation
      await new Promise(resolve => setTimeout(resolve, 300));
      
      // Each batch waits for all its items to complete before starting the next batch
      return {
        processed: true,
        value: item * 10,
        timestamp: new Date().toISOString()
      };
    } catch (error) {
      // Error handling with async/await
      console.error(`Error processing item ${item}:`, error);
      throw error; // Re-throw to trigger error handling in SeqProm
    }
  },
  finalCB(errors, responses) {
    console.log(`Processed ${responses.length} items in ${Math.ceil(responses.length/2)} batches`);
    
    // Group responses by batch (for demonstration purposes)
    const batches = [];
    for (let i = 0; i < responses.length; i += 2) {
      batches.push(responses.slice(i, i + 2));
    }
    
    // Show each batch's results
    batches.forEach((batch, i) => {
      console.log(`Batch ${i+1} results:`);
      batch.forEach(res => {
        console.log(`- Item ${res.item} → ${res.result.value} at ${res.result.timestamp}`);
      });
    });
  }
});

Note: In batch mode, all items in a batch are processed concurrently, but the next batch only starts after the current batch completely finishes processing.

Functions

cb

| Argument | Description | |----------|--------------------------------------------------| | item | Item from the list (type T) | | extra | Object containing resolve, reject, and self | | extra.resolve | The resolve function (accepts value of type RT) | | extra.reject | The reject function for error handling | | extra.self | Reference to the SeqProm instance (can call .stop()) |

errorCB

| Argument | Description | |----------|-----------------------------------------------------------------------| | item | Item from the list | | reason | Reason for the error, either from the reject method or a caught error |

finalCB

| Argument | Description | |-----------|---------------------------------------------------| | error | List of errors that have occured | | response | List of items passed back to the resolve function |

Promise Handling

import SeqProm from "seq-prom";

// Using the promise directly with typed response
SeqProm<number, string>({
  list: [1, 2, 3],
  autoStart: true,
  cb(item, {resolve, reject}) {
    console.log(`Item [${item}] called!`);
    setTimeout(function () {
      if (item === 3) {
        return reject("Too large");
      } else {
        // Converting numbers to strings as our return type is string
        return resolve(`Value: ${item}`);
      }
    }, item * 1000);
  }
})
.promise
.then(([errors, responses]) => {
  // errors: Array<{item: number, reason: string | Error}>
  // responses: Array<{item: number, result: string}>
  console.log(errors, responses);
  
  // You can access the successful results
  responses.forEach(response => {
    console.log(`Item ${response.item} resulted in: ${response.result}`);
  });
});

Type System

Generic Type Parameters

SeqProm is fully type-safe and supports generic type parameters for both input items and return values.

SeqProm takes two type parameters:

SeqProm<ReturnType, ItemType>({ ... })
  • ReturnType - The type of value returned by your callback function
  • ItemType - The type of items in your input list

Example with Complex Types

// Define custom types
interface User {
  id: number;
  name: string;
}

interface ProcessedUser {
  id: number;
  displayName: string;
  lastProcessed: Date;
}

// Using SeqProm with complex types
const users: User[] = [
  { id: 1, name: "Alice" },
  { id: 2, name: "Bob" },
  { id: 3, name: "Charlie" }
];

SeqProm<ProcessedUser, User>({
  list: users,
  size: 2,  // Process 2 users concurrently
  cb(user, {resolve}) {
    // Transform User to ProcessedUser
    const processed: ProcessedUser = {
      id: user.id,
      displayName: user.name.toUpperCase(),
      lastProcessed: new Date()
    };
    resolve(processed);
  },
  finalCB(errors, processedUsers) {
    // processedUsers is typed as Array<{item: User, result: ProcessedUser}>
    processedUsers.forEach(item => {
      console.log(
        `User ${item.item.name} processed as ${item.result.displayName} at ${item.result.lastProcessed}`
      );
    });
  }
}).start();

Callback Styles

Direct Return Value Style

// Simply return a value from the callback
SeqProm({
  list: ['apple', 'banana', 'cherry'],
  cb(item) {
    // Return directly - SeqProm handles this automatically
    return item.toUpperCase();
  }
});

Async/Await Style

// Using async/await for clean asynchronous code
SeqProm({
  list: [1, 2, 3],
  cb: async (item) => {
    // Use await for any async operations
    const result = await someAsyncOperation(item);
    return result;
  }
});

// Helper function
async function someAsyncOperation(value: number): Promise<string> {
  return new Promise(resolve => {
    setTimeout(() => resolve(`Processed ${value}`), 100);
  });
}

Promise Chain Style

// Returning a Promise directly
SeqProm({
  list: [1, 2, 3],
  cb(item) {
    return new Promise<string>(resolve => {
      setTimeout(() => resolve(`Number: ${item}`), 100);
    });
  }
});

Resolve/Reject Style (Traditional)

// Using the provided resolve/reject functions
SeqProm<string, number>({
  list: [1, 2, 3],
  cb(item, {resolve, reject}) {
    if (item > 2) {
      reject('Value too large');
    } else {
      resolve(`Number: ${item}`);
    }
  }
});

Advanced Examples with Complex Types

Working with Complex Promise Chains

// Define our response type structure
interface ApiResponse<T> {
  data: T;
  timestamp: number;
  success: boolean;
}

interface UserData {
  id: number;
  name: string;
  email: string;
}

// Process a list of user IDs with complex Promise handling
SeqProm<ApiResponse<UserData>, number>({
  list: [101, 102, 103],
  size: 2, // Process 2 at a time
  cb(userId) {
    // Return a typed Promise chain
    return fetchUserById(userId)
      .then(userData => {
        // Transform the data into our ApiResponse format
        return {
          data: userData,
          timestamp: Date.now(),
          success: true
        };
      })
      .catch(error => {
        // Handle errors in the Promise chain
        console.error(`Failed to fetch user ${userId}:`, error);
        throw new Error(`User fetch failed: ${error.message}`);
      });
  },
  finalCB(errors, responses) {
    // Type-safe access to the complex response structure
    responses.forEach(res => {
      const userData = res.result.data;
      console.log(
        `User ${userData.name} (${userData.email}) fetched at ${new Date(res.result.timestamp).toLocaleString()}`
      );
    });
  }
});

// Mock API function
function fetchUserById(id: number): Promise<UserData> {
  return new Promise((resolve, reject) => {
    // Simulate API call
    setTimeout(() => {
      if (id === 102) {
        reject(new Error("User not found"));
        return;
      }
      
      resolve({
        id,
        name: `User ${id}`,
        email: `user${id}@example.com`
      });
    }, 200);
  });
}

Tests

npm test

Release History

  • 3.0.0 ReadMe update
  • 3.0.0 TypeScript update
    • Full TypeScript rewrite with generic type support
    • Enhanced error handling and promise support
    • Renamed parameters for clarity (batchSize -> size, useStream -> useBatch)
    • Improved test coverage and fixed Promise resolution issues
    • Support for direct return values, Promises, and resolve/reject callbacks
  • 1.1.1 Package update
  • 1.1.0
    • Updates to allow passage of data
    • Added more tests
    • Added autStart option
    • Added ability to chain off of promise
  • 1.0.0 Initial release