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 🙏

© 2024 – Pkg Stats / Ryan Hefner

array-batcher

v1.2.3

Published

Execute operations on or mapping conversions of JavaScript Arrays in predetermined batches. Batches can be of a fixed size, created from a desired number of batches, or constructed by iterating through the array and testing a predicate.

Downloads

305

Readme

array-batcher

A Node.JS module that provides utility functions to execute synchronous or asynchronous operations on or mapping conversions of JavaScript Arrays in predetermined batches. Batches can be of a fixed size, created from a desired number of batches, or constructed by iterating through the array and testing a predicate.

Installation

npm install array-batcher --save

Usage

Javascript

import { BatcherAgent } from "array-batcher";

// In this case, an odd number of elements yields a best approximation of the even-numbered batch size 
const batched = new BatcherAgent(["this", "is", "not", "a", "test"]).fixedBatch({ batchSize: 2 });
console.log(batched);
Output should be:
[["this", "is"], ["not", "a"], ["test"]]

TypeScript

import { BatchedArray } from 'array-batcher';

// helper function to track elapsed time
const benchmark = (reference = 0) => new Date().getTime() - reference;
// helper function to simulate waiting for the given duration, i.e. in a request, etc.
const wait = (duration) => new Promise(resolve => setTimeout(resolve, duration));

/**
 * This example demonstrates one potential use case for the asynchronous batching
 * capabilities of the module, while showing the differences between patient
 * and strict interval batching.
 *
 * While the example uses a hard coded array of animal objects below, imagine that the input array
 * has been dynamically generated, and thus has an unknown number of unknown animals at the time
 * of execution.
 *
 * The goal is to upload these images to a server via a series of HTTP POST requests to a REST API.
 * In this example, let's say you've looked at the API's documentation and found to our dismay that the server can
 * process only up to a certain number of bytes in any one request. How can you, in one function call,
 * dynamically divide this unknown list of images into appropriately sized groups that can be sent to the API?
 *
 * Here's where the dynamic, asynchronous batching comes in. Continue reading inline below...
 */
async function UploadDispatcherSimulator(threshold: number, expected: number, patient = true) {
    const cow = { name: "Cow", weight: 2000, lifespan: 20, image: "https://cdn.britannica.com/55/174255-050-526314B6/brown-Guernsey-cow.jpg" };
    const sparrow = { name: "Sparrow", weight: 0.0625, lifespan: 3, image: "https://www.thespruce.com/thmb/X31KQaI5ttNpFE9ho8JLrJj258A=/1500x1000/filters:no_upscale():max_bytes(150000):strip_icc()/eurasian-tree-sparrow-5a11f1630d327a00367c025a.jpg" };
    const shark = { name: "Shark", weight: 2400, lifespan: 30, image: "https://cbsnews1.cbsistatic.com/hub/i/2012/09/03/23633c73-a645-11e2-a3f0-029118418759/greatwhiteshark.jpg" };

    // Here, use the static, asynchronous constructor to batch the list of animals, and as a
    // second argument, pass in the function used to generate the desired batches.
    // Though the ThresholdAsync() returns a pre-built and packaged example of an asynchronous predicate batcher
    // that serves this category of use case well, you could pass in any asynchronous predicate batcher (i.e. custom function)
    // of your choosing
    const target = await BatchedArray.fromAsync([cow, sparrow, shark], ThresholdAsync(threshold, async animal => {
        const metadata = await new Promise((resolve, reject) => {
            // To start building batches, you must know something about each image's size
            // So, consider using request-promise to retrieve each image's byte count
            // It's no problem that this request is asynchronous! Simply await the
            // single promise that represents the construction of the array. When it resolves, all the batches
            // will have been created
            request.head(animal.image, (error, response) => {
                if (error) {
                    return reject(error);
                }
                resolve(response);
            });
        });
        return Number(metadata.headers["content-length"]);
    }));

    // Having viewed the output of these sizing requests, I've determined how many batches each
    // threshold should produce to determine this assertion test. Generally, in cases like these, the expected batch counts would
    // not be knowable before batching. And that's the point!
    expect(target.batchCount).to.equal(expected);

    const reference = new Date().getTime();
    
    // Here, declare that you'd like to allocate three seconds between the execution of each batch,
    // as well as defining the callback to be invoked on each batch
    // For example, the first batch will be passed into our handler instantaneously, then the second after 3 seconds, etc.
    // (exact timing depends on strict vs. patient intervals)
    const interval: Interval.Instance = { magnitude: 3, unit: TimeUnit.Seconds };
    const handler = async animals => {
        console.log(`Dispatching upload for ${animals.map(animal => animal.name)} at ${benchmark(reference)} ms`);
        // Simulates the time taken to receive a response from the remote server
        // Note that it has no bearing on the strict benchmarks, but a fair effect on the patient
        await wait(1000 * (1 + Math.random()));
    };
    
    if (patient) {
        // With a patient interval, the next batch cannot proceed until the promise associated with the
        // previous batch resolves. Thus, the interval specified will add an *additional* chronological padding
        // between the resolution of the previous promise (which could have taken arbitrary time) and the dispatch
        // of the next. This is best if, say, you want to be sure that a remote server has completed processing a request
        // before dispatching the next
        await target.batchedForEachPatientInterval(interval, handler);    
    } else {
        // With a strict interval, each batch is executed exactly at the specified interval, regardless
        // of the state of the previous promise. This is best for scheduling UI events or requests where
        // one does not care whether or not the previous event, request, etc. has completed
        await target.batchedForEachNaiveInterval(interval, handler);   
    }
}

async function ExecuteUploadSimulations() {
    const megabytes = 1000000;

    console.log("\nPATIENT");

    console.log(`\nPatient test with a 1 megabyte threshold!`);
    await UploadDispatcherSimulator(1 * megabytes, 1);

    console.log(`\nPatient test with a 500 kilobyte threshold!`);
    await UploadDispatcherSimulator(0.5 * megabytes, 2);

    console.log(`\nPatient test with a 200 kilobyte threshold!`);
    await UploadDispatcherSimulator(0.2 * megabytes, 3);

    console.log("\nSTRICT");

    console.log(`\nStrict test with a 1 megabyte threshold!`);
    await UploadDispatcherSimulator(1 * megabytes, 1, false);

    console.log(`\nStrict test with a 500 kilobyte threshold!`);
    await UploadDispatcherSimulator(0.5 * megabytes, 2, false);

    console.log(`\nStrict test with a 200 kilobyte threshold!`);
    await UploadDispatcherSimulator(0.2 * megabytes, 3, false);
}

ExecuteUploadSimulations()
Where ~ denotes approximately, output should be:

PATIENT

Patient test with a 1 megabyte threshold!
Dispatching upload for Cow,Sparrow,Shark at ~1 ms

Patient test with a 500 kilobyte threshold!
Dispatching upload for Cow,Sparrow at ~0 ms
Dispatching upload for Shark at ~4454 ms

Patient test with a 200 kilobyte threshold!
Dispatching upload for Cow at ~0 ms
Dispatching upload for Sparrow at ~4732 ms
Dispatching upload for Shark at ~9183 ms

STRICT

Strict test with a 1 megabyte threshold!
Dispatching upload for Cow,Sparrow,Shark at ~0 ms

Strict test with a 500 kilobyte threshold!
Dispatching upload for Cow,Sparrow at ~0 ms
Dispatching upload for Shark at ~3004 ms

Strict test with a 200 kilobyte threshold!
Dispatching upload for Cow at ~0 ms
Dispatching upload for Sparrow at ~3003 ms
Dispatching upload for Shark at ~6007 ms

Test

npm run test