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

promises-arrow

v1.3.1

Published

`promises-arrow` is a library of (1) higher order functions, such as `map()` and `filter()`where the function returns a Promisel (2) Functions that wait, returning a Promise; (3) Functions that manage retrying an operation N times until it succeeds; (4,5)

Downloads

867

Readme

promises-arrow

promises-arrow is a library of:

  • Higher-order over collections where the function returns a Promise: forEach(), map(), filter(), etc.
  • Functions that wait, returning a Promise.
  • Functions that manage retrying an operation N times until it succeeds.
  • SlidingWindow, which manages a sliding window producer, which limits the work in progress.
  • DeferredPromise, which creates a Promise in one context that is resolved/rejected in another.

Higher-order over collections

These functions deal correctly with composing the Promises that result in a memory-efficient manner. So a huge collection doesn't create a huge stack of recursive calls.

+ map(): <T, U>(items: Array<T>, fn: (item: T) => Promise<U>) => Promise<Array<U>>

This takes an array of some items, plus a function (fn) that is applied to each of the elements in turn. That fn returns a Promise.

Eg, this could be used to take an array of some data, where the fn calls an external service with that data, returning the result in a Promise. Eg, in an async function:

    const mappedResults = await promises.map(collectionOfData, data => service.call(data))

+ flatMap(): : <T, U>(items: Array<T>, fn: (item: T) => Promise<Array<U>>) => Promise<Array<U>>

This takes an array of some items, plus a function (fn) that is applied to each of the elements in turn. That fn returns a Promise containing an array.

Eg, this could be used to take an array of some data, where the fn calls an external service with that data, returning the results in a Promise. Eg, in an async function:

    const flatMappedResults = await promises.flatMap(collectionOfData, data => service.callMultiple(data))

+ filter: <T>(items: Array<T>, fn: (item: T) => Promise<boolean>) => Promise<Array<T>>

This takes an array of some items, plus a function (fn) that is applied to each of the elements in turn. That fn returns a Promise containing a boolean.

Eg, this could be used to take an array of some data, where the fn calls an external service with that data, returning whether some condition is true in a Promise. Eg, in an async function:

    const filteredResults = await promises.filter(collectionOfData, data => service.predicate(data))

+ forEach: <T>(items: Array<T>, fn: (item: T, index: number) => Promise<any>) => Promise<any>

This takes an array of some items, plus a function (fn) that is applied to each of the elements in turn for some side-effect. That fn returns a Promise that is unlikely to have a value.

Eg, this could be used to take an array of some data, where the fn passes that data to an external service, returning a Promise. Eg, in an async function:

    await promises.forEach(collectionOfData, data => service.command(data))

+ forEachIterator: <T>(it: Iterator<T>, fn: (item: T, index: number) => Promise<any>) => Promise<any>

Like forEach, except where the items are provided by an Iterator.

+ forEachWithConstrainedParallelism: <T, U>(items: Array<T>, asynchCount: number, fn: (item: T) => Promise<unknown>)

Run the functions partially in parallel, allowing up to asynchCount of them to be in progress at once. The result can't be depended on. This works well for Promises that resolve on completion of external activity, such as an HTTP GET. Eg:

     await promises.forEachWithConstrainedParallelism(collectionOfData, 5, data => service.command(data))

+ for: <T>(start: number, pastEnd: number, increment: number, fn: (t: number) => Promise<any>) => Promise<any>

Loop from start up to pastEnd, incrementing by the increment. For each number, call the function, which returns a Promise. Eg, forEach() is defined in terms of for():

    static forEach<T>(items: Array<T>, fn: (item: T) => Promise<any>): Promise<any> {
        return promises.for(0, items.length, 1, i => fn(items[i]));
    }

+ while: <T>(fnContinue: () => boolean, fn: () => Promise<unknown>) =>Promise<unknown>

Continue looping while 'fnContinue()' is true. For each loop, call the function, which returns a Promise. Eg, for() is defined in terms of while():

   static for<T>(start: number, pastEnd: number, increment: number,
                  fn: (t: number) => Promise<any>): Promise<any> {
        let value = start;
        return promises.while(
            () => value < pastEnd,
            () => fn(value).then(() => value += increment)
        );
    }

Waiting and Promises

+ waitForPromise: <T>(milliseconds: number, value?: T) => Promise<T>

Returns a Promise after a delay. The delay is the first argument as the value is optional. Eg:

    await promises.waitForPromise(5);
    const result = await promises.waitForPromise(5, 'result');

+ waitForTimeoutOrPromise: <T>(timeout: number, fn: () => Promise<T>) => Promise<T>

Waits for a Promise to resolve but only for a timeout period. If the Promise rejects or the timeout is exceeded, the resulting Promise is rejected. Eg:

    await result  = promises.waitForTimeoutOrPromise(5, () => dbRepository.get(id));

Retry

+ retryOverExceptions<T>(fn: () => Promise<T>, logger: (message: any) => void, retries = 3, timeout = 100): Promise<T>

  • This runs the fn and returns its result.
  • However, if an exception is thrown while executing fn, it is retried up to retries times
    • Each time it retries, it first delays for timeout milliseconds, which is doubled each time for exponential back-off.

For example, this is useful for dealing with:

  • Temporary exceptions on calls to other services
  • Temporary exceptions on DB updates due to competing updates

+ retryOnTimeout<T>(fn: () => Promise<T>, logger: (message: any) => void, retries = 5, timeout = 100, allowedException: (e) => boolean = () => false): Promise<T>

Similar to retryOverExceptions, except that it also allows for timeouts.

  • This runs the fn and returns its result.
  • However, if an allowed exception is thrown while executing fn, it rejects immediately. This is useful for example, when we expect to get an exception to signal EOF.
  • However, if an exception that is not allowed is thrown while executing fn, it is retried up to retries times.
    • Each time it retries, it first has a timeout delay, which is doubled each time for exponential back-off.

For example, this is useful for dealing with:

  • Temporary exceptions or timeouts on calls to other services
  • Calls that result in an exception to signal end of processing, such as when reading bytes from S3

+ retryOnTimeoutGivingFirstResult<T>(fn: () => Promise<T>, logger: (message: any) => void, retries = 5, timeout = 100): Promise<T>

Similar to retryOnTimeout, except that:

  • It returns the first value returned by the fn, event after it has timed out once
  • It does not have a notion of an allowed exception. Instead, it retries after an exception (up to the number of retries)

Specifically:

  • This runs the fn and returns its result.
  • However, if that times out, it tries again. But if an earlier call returns a value, that will be returned.
  • However, if an exception is thrown while executing fn, it is retried up to retries times.
    • Each time it retries, it first has a timeout delay, which is doubled each time for exponential back-off.

+ retryUntilValid<T>(fn: () => Promise<T | undefined>, valid: (value: T | undefined) => boolean, logger: (message: any) => void, retries = 5, timeout = 100): Promise<Option<T>>

  • It returns the first value returned by the fn() that satisfies valid(). The value is returned as a Some.
  • It retries the given number of time, with exponential backoff on delays (timeout), and if unsuccessful, returns a None

Specifically:

  • This runs the fn and returns its result.
  • However, if that times out, it tries again. But if an earlier call returns a value, that will be returned.
  • However, if an exception is thrown while executing fn, it is retried up to retries times.
    • Each time it retries, it first has a timeout delay, which is doubled each time for exponential back-off.

+ poll<T>(fn: () => Promise<Option<T>>, retries = 3, timeout = 100): Promise<Option<T>>

  • This runs the fn() and returns its result if it's a Some value.
  • A None value means there is not yet a value from the poll. So it tries again. The timeout delay is doubled each time for exponential back-off.
  • However, if it has retried up to retries times, it returns a result of None.
  • Any exceptions thrown by the fn() are not caught.

SlidingWindow

Writing/sending with a sliding window means that only so many sends can be in progress. When a send completes when the sliding window is full, another send can be initiated.

Communication is with a SlidingWindowProducer that manages the actual sending process.

To be documented. See the micro tests for useful examples of use.

DeferredPromise

+ deferredPromise: <T>() => DeferredPromise<T>

Creates a Promise in one context that is resolved/rejected in another. SlidingWindow is an example of use, where new Promise((resolve, reject) => {...}) is insufficient.