GithubHelp home page GithubHelp logo

blend / promise-utils Goto Github PK

View Code? Open in Web Editor NEW
280.0 7.0 15.0 606 KB

Lodash-like, dependency-free utilities for native ES6 promises.

License: MIT License

JavaScript 9.04% Shell 0.33% TypeScript 88.00% Makefile 2.63%
nodejs typescript promises javascript lodash dependency-free blend

promise-utils's Introduction

promise-utils

Build Status Coverage Status Minzipped size

Promise-utils is a dependency-free JavaScript/TypeScript library that provides Lodash-like utility functions for dealing with native ES6 promises.

Installation

$ npm install blend-promise-utils

Usage Example

const promiseUtils = require('blend-promise-utils')
const { promises: fs } = require('fs')
const request = require('request-promise-native');
const isEmpty = require('lodash.isempty');

const MS_IN_SECOND = 1000;

async function main() {
  const cachedResponse = promiseUtils.memoize(
    async (contents) => request(contents.url),
    contents => contents.url,
    15 * MS_IN_SECOND // contents could change
  );

  const fileContents = await promiseUtils.map(
    ['file1', 'file2', 'file3'],
    async fileName => {
      const rawData = await fs.readFile(fileName);
      return JSON.parse(rawData);
    },
  );

  while (true) {
    await promiseUtils.delay(150); // avoid slamming CPU

    await promiseUtils.mapSeries(
      fileContents,
      async contents => {
        const remoteData = await cachedResponse(contents);

        const { results, errors } = await promiseUtils.settleAll([
          asyncFunction1(),
          asyncFunction2(),
          asyncFunction3(),
        ]);

        if (!isEmpty(errors)) {
          throw new Error(`Unable to settle all functions: ${JSON.stringify(errors)}`);
        } else {
          return results;
        }
      }
    )
  }

  await promiseUtils.retry(flakyFunction, { maxAttempts: 3, delayMs: 150 })(flakyFunctionArgument);

  await promiseUtils.timeout(longFunction, 60 * MS_IN_SECOND)(longFunctionArgument);
}

main()

API

Test

$ npm test

Documentation

Build docs

$ make docs

Push docs to Github

$ make push-docs

License

MIT

promise-utils's People

Contributors

akarimcheese avatar apmyp avatar benatblend avatar bertyhell avatar dependabot[bot] avatar dhermes avatar eugeneiiim avatar ftrimble avatar gcarling avatar kanggg avatar nadavoosh avatar shawnjones253 avatar wzb3422 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

promise-utils's Issues

Preserve order of settleAll results if error occurs

Hi,

Current behavior of settleAll function is that if some promise throws an error, it is removed from the results array and added to errors array. This way the order of promises is broken and additionally, it's hard to guess, from which promise the errors comes. E.g.

  /* As an example, secondPromise() throws an error */
  const {
    results: [
      firstPromiseResult,
      secondPromiseResult, // it contains thirdPromiseResult
      thirdPromiseResult, // it is undefined
    ],
    errors, // it contains array of single error of the second promise
  } = await settleAll([
    firstPromise(),
    secondPromise(),
    thirdPromise(),
  ]);

To my mind, it would be much more intuitive if the order of resolved promises and errors preserved:

  /* As an example, secondPromise() throws an error */
  const {
    results: [
      firstPromiseResult,
      secondPromiseResult, // it contains null/undefined
      thirdPromiseResult, // it contains thirdPromiseResult
    ],
    errors, // it contains [null, secondPromiseError, null]
  } = await settleAll([
    firstPromise(),
    secondPromise(),
    thirdPromise(),
  ]);

Wrong implementation for settleAll function

The settleAll utility function should accept an array of async functions (or functions that returns promises), not an array of promises because it would cause triggering the needed functions before the right time and that is problematic...

Current implementation (based on docs):

        const { results, errors } = await promiseUtils.settleAll([
          asyncFunction1(),
          asyncFunction2(),
          asyncFunction3(),
        ]);

The right way for things to work:

        const { results, errors } = await promiseUtils.settleAll([
          asyncFunction1,
          asyncFunction2,
          asyncFunction3,
        ]);

Readme - filtering when it should be mapping

  const fileContents = await promiseUtils.filter(
    ['file1', 'file2', 'file3'],
    async fileName => {
      const rawData = await fs.read(fileName);
      return JSON.parse(rawData);
    },
  );

This snippet from the readme looks like it should be using promiseUtils.map, as there is no predicate/filtering, unless the filtering is "only keep the files that the raw data parses into something." If the latter is the case, for documentation I'd recommend refactoring this to something that clearly returns a boolean

Sourcemaps broken ...

Hi. Nice library!

I've found a bug where when trying to compile with parcel, it throws warnings: like:

⚠️  Could not load source file "../../src/index.ts" in source map of "../../node_modules/blend-promise-utils/dist/src/index.js".
⚠️  Could not load source file "../../src/delay.ts" in source map of "../../node_modules/blend-promise-utils/dist/src/delay.js".
⚠️  Could not load source file "../../src/invert.ts" in source map of "../../node_modules/blend-promise-utils/dist/src/invert.js".
⚠️  Could not load source file "../../src/retry.ts" in source map of "../../node_modules/blend-promise-utils/dist/src/retry.js".
⚠️  Could not load source file "../../src/settleAll.ts" in source map of "../../node_modules/blend-promise-utils/dist/src/settleAll.js".
⚠️  Could not load source file "../../src/timeout.ts" in source map of "../../node_modules/blend-promise-utils/dist/src/timeout.js".
⚠️  Could not load source file "../../src/memoize.ts" in source map of "../../node_modules/blend-promise-utils/dist/src/memoize.js".
⚠️  Could not load source file "../../src/filter.ts" in source map of "../../node_modules/blend-promise-utils/dist/src/filter.js".
⚠️  Could not load source file "../../src/map.ts" in source map of "../../node_modules/blend-promise-utils/dist/src/map.js".
⚠️  Could not load source file "../../src/errors.ts" in source map of "../../node_modules/blend-promise-utils/dist/src/errors.js".

When looking in the dist folder, the *.js.map files shows the entrypoint to a file that does not exist in the build.

sources":["../../src/delay.ts"]

Solution1: Include the source in the npm package.
Solution2: Remove the sourcemaps?

`defer` function inspired by `useDeferredValue` hook in React

Description

This is a feature request for a defer function that returns a value of the last invocation of a function. It's similar to caching/throttling/debouncing but I couldn't find any equivalent in this library or in Lodash. So I'm proposing this to see if any other people have similar use cases.

export const defer = <T, F extends (...args: never[]) => Promise<T>>(
  callback: F
): ((...args: Parameters<F>) => Promise<T>) => {
  let cache: Promise<T> | undefined = undefined;

  return async (...args: Parameters<F>): Promise<T> => {
    const lastCache = cache;
    cache = callback(args);

    return lastCache ?? cache;
  };
};

it("defers a value", async () => {
  const callback = defer(async (x: number) => x);

  expect(await callback(1)).toBe(1);
  expect(await callback(2)).toBe(1);
  expect(await callback(3)).toBe(2);
  expect(await callback(4)).toBe(3);
});

References

Doesn't work with Typescript

The page says this is a javascript / typescript library but it isn't. It's pure javascript. There is zero type information when I install this into my project.

This is surprising since I see it is written with Typescript.

[Bug] flatMap() maps empty arrays into the returned value

flatMap should not map an empty array into the result, empty arrays should be ignored as per ECMA implementation of Array.prototype.flatMap

RunKit link

const { flatMap } = require("blend-promise-utils")

let input = [{
    data: []
}, {
    data: [1,2,3]
}]

const itaratee = input => input.data
const expected = input.flatMap(itaratee)
const actual = await flatMap(input, itaratee)
console.log('Expected: ' + JSON.stringify(expected))
console.log('Actual: ' + JSON.stringify(actual))
"Expected: [1,2,3]"
"Actual: [[],1,2,3]"

Add a '.throttle' method

I seem to have this need every so often and keep finding myself redefining the same function over and over again in multiple projects. The point of the function is to take an array of functions that produce a promise but will only start 10 (configurable) promises at a time.

A real-world example I've had recently was to download all the URLs listed in a file, with no more than 10 requests at a time:

  const fileData = await fs.promises.readFile('urls.txt', 'utf-8') as string;
  const fileLines = fileData.split('\n');

  const promiseFuncs = fileLines.map((l) => () => downloadUrl(l));
  await promiseThrottle(promiseFuncs, 10);

The code for promiseThrottle looks like this:

export async function promiseThrottle(promiseFuncs: any[], max = 10) {
  const executingPromises = [];
  const results = [];
  while (executingPromises.length !== 0 || promiseFuncs.length !== 0) {
    while (executingPromises.length < max && promiseFuncs.length !== 0) {
      const promise = promiseFuncs.shift()().then((result: any) => {
        promise.done = true;
        return result;
      });
      executingPromises.push(promise);
    }

    results.push(await Promise.race(executingPromises));
    executingPromises.splice(executingPromises.findIndex((p) => p.done), 1);
  }

  return results;
}

In the above example, downloadUrl saves to disk as a side-effect (because there were 1000s of web pages) but it could also return its results when all promises have been run.

Do you think there's a place for something like this functionality in promiseUtils ?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.