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

concurrentfetcher

v2.0.2

Published

A javascript class for managing concurrent fetch requests.

Readme

ConcurrentFetcher

A javascript class for managing concurrent fetch requests.

The Fetch Web API is a neat tool for fetching/getting network resources in web applications. And although fetch() is generally easy to use, there are a few nuances in regards to error handling, asynchronous and concurrent processing, cancellation and so forth :-)

The ConcurrentFetcher class addresses the core challenges of concurrent requests, error handling, large data, and controlled cancellation.

Availability

Maintained at github : https://github.com/asicscreed/ConcurrentFetcher And published at npm: https://www.npmjs.com/package/concurrentfetcher

Install

npm install concurrentfetcher

Usage

Basically, you instantiate the class with an array of fetch requests and then call concurrentFetch(). It calls fetch and consumes the Response object. If a callback is defined, it is called for each response. Without a callback, the responses (data and errors, respectively) are collected and returned. Like this:

const requests = [
 { url: "https://jsonplaceholder.typicode.com/photos/1" },
 { url: "https://jsonplaceholder.typicode.com/comments/1" }
];
const fetcher = new ConcurrentFetcher.ConcurrentFetcher(requests);
fetcher.concurrentFetch()
.then((fetchResults) => {
    const reasons = fetchResults.filter(arr => arr.status === 'rejected');
    // NB! Do not filter the fetchResults array on large objects
    const values = fetchResults.filter(arr => arr.status !== 'rejected');
})

The result is an array of objects, each describing the outcome of one promise in the iterable, in the order of the promises passed, regardless of completion order. This is handled by Promise.allSettled(). Each outcome object has the following properties:

// for successful results:
{ status: 'fulfilled',
  value: {
    id: /* unique id which identifies the request */,
    stamp: /* timespamp set when request finished */,
    data: /* data returned from the fetch request. Either text, json or blob data. */
 }
}
// for errors:
{ status: 'rejected',
  reason: {
    id: /* unique id which identifies the request */,
    stamp: /* timespamp set when request finished */,
    error: /* catched from the failed fetch request. */
  }
}
// NB! When callbacks are being used, then data and error er left out (since they are available for the callback)

Since Promise.allSettled() only returns when all requests have been completed: resolved and/or rejected, Promise.all() is being used, when the approach is more all or nothing.

ConcurrentFetcher supports the option of aborting all further processing on the first error. This is to mimic the behavior of Promise.all(). The boolean named parameter abortOnError for the concurrentFetch() method controls this. If set and an error occurs, all further processing is aborted. The final response is - though - as Promise.allSettled() complete with all resolved and rejected responses. To identify the initial/first error raised, the instance method getErrorRaised() - returns a single reason object: { id, stamp, error } - as above.

Browser example without callback (src="concurrentfetcher.iife.min.js"):

JavaScript: Example1

const requests = [
 { url: "https://jsonplaceholder.typicode.com/photos/1" },
 { url: "https://jsonplaceholder.typicode.com/comments/1" }
];
const fetcher = new ConcurrentFetcher.ConcurrentFetcher(requests);
fetcher.concurrentFetch({ abortOnError: true })
.then((fetchResults) => {
  if (fetcher.getErrorRaised()) { console.log(fetcher.getErrorRaised()); }
  for (let i = 0; i < fetchResults.length; i++) {
    if (fetchResults[i].status === 'rejected') {
      const { id, stamp, error } = fetchResults[i].reason;
      document.write(error.toString());
    } else {
      const { id, stamp, data } = fetchResults[i].value;
      document.write(JSON.stringify(data));
    }
  }
})

Same currentFetch example, but with callback (src="concurrentfetcher.iife.min.js"):

JavaScript: Example2

const requests = [
  { url: "https://jsonplaceholder.typicode.com/photos/1",
    callback: (uniqueId, data, error, abortManager) => {
      if (error) document.write(JSON.stringify(error));
      else document.write(JSON.stringify(data));
    }
  },
  { url: "https://jsonplaceholder.typicode.com/comments/1",
    callback: (uniqueId, data, error, abortManager) => {
      if (error) document.write(JSON.stringify(error));
      else document.write(JSON.stringify(data));
    }
  }
];

const fetcher = new ConcurrentFetcher.ConcurrentFetcher(requests);
fetcher.concurrentFetch()
...

Hosted in Node.js with: concurrentfetcher.umd.min.js

JavaScript: Node.js

const people  = document.getElementById('people');
const fetcher = new ConcurrentFetcher.ConcurrentFetcher(requests);
fetcher.concurrentFetch()
.then((fetchResults) => {
  const errors = fetchResults.filter(answer => answer.status === 'rejected');
  if (errors.length > 0) { /* Do something about the errors */ }
  const results = fetchResults.filter(answer => answer.status !== 'rejected');
  if (results.length > 0) {
    people.innerHTML = results[0].value.data.map((person) => {
      return (
        '<div class="text-center mt-3">'+
          '<h5 class="mt-2 mb-0">'+person.name+'</h5>'+
          '<span>'+person.email+'</span>'+
          '<div class="px-4 mt-1">'+
            '<p class="fonts">'+person.company.catchPhrase+'</p>'+
          '</div>'+
        '</div>'
      );
    }).join('');
  } // data.results.length
})
.catch(error => console.error(error));  

Loaded in RequireJS with: concurrentfetcher.amd.min.js

JavaScript: RequireJS

requirejs.config({ paths: { ConcurrentFetcher: '/concurrentfetcher.amd.min' }});
requirejs(['ConcurrentFetcher'], function (ConcurrentFetcher) {
  const requests = [
      {
          url: 'https://api.github.com/users/asicscreed',
          headers: {
            Accept: "application/json",
            "Content-Type": "application/json",
          },
      }
  ];
  const fetcher = new ConcurrentFetcher.ConcurrentFetcher(requests);
  fetcher.concurrentFetch()
  .then((fetchResults) => {
    const errors = fetchResults.filter(answer => answer.status === 'rejected');
    if (errors.length > 0) { /* Do something about the errors */ }
    const results = fetchResults.filter(answer => answer.status !== 'rejected');
    if (results.length > 0) {
      user = results[0].value.data;
      document.getElementById('useravatar').src = user.avatar_url;
      document.getElementById('username').innerHTML = user.login;
    }
  })

Documentation:

Currently, all documentation is located in the docs folder. It is generated by JSDoc, and is therefore regular html documents.

Key points:

Concurrency: The class leverages Promise.all() for efficient concurrent execution and optimizing performance. Error handling: Custom error classes (FetchError, JsonParseError) and error reporting provide information for debugging and handling failures. Flexibility: The ability to configure individual fetch options and utilize both callbacks and promises makes the class adaptable to various use cases. Cancellation: The AbortManager class provides robust cancellation support, allowing for individual and global request cancellation. Support for client-controlled cancellation of a single request and of all requests. And timeout controlled cancellation on individual requests. Progress tracking: The optional progressCallback enables monitoring the progress of fetch requests. Retry logic: Retry mechanism for failed requests to improve the resilience of the class. Large data handling: Utilizes response.body to read large data in chunks. Reports byte transfer information for progress tracking. Testing: Extensive unit tests to improve the classes reliability.

Areas for consideration:

Advanced progress Tracking: ~~Implement more granular progress tracking~~, such as byte transfer information for more detailed monitoring. Stream handling: ~~Adding support for handling response streams, which~~ would be beneficial for large data transfers. Testing: ~~(More) Extensive unit tests and~~ integration tests will improve the classes reliability further. Adaptability: Examples to demonstrate how to use the class in various environments (~~browser~~, ~~Node.js~~, frontend frameworks, ~~testing~~).


Get help: Post in our discussion boardReview the GitHub status page

© 2023 GitHub • Code of ConductMIT License