How to handle the completion of multiple asynchronous functions in JavaScript

Hey there, I have a challenge that involves making a series of AJAX requests to a server and then performing a final request using the data received from the earlier requests. It's essential for me to ensure that the previous requests are completed before initiating the final one, all while maintaining a sequential order to avoid overwhelming the server. Unfortunately, I'm encountering difficulties trying to implement this in JavaScript.

In an attempt to simulate this behavior, I created a simple test script with sleep intervals:

const sleep = (delay) => new Promise((resolve) => setTimeout(resolve, delay));

var urls = ['1', '2', '3'];

const slowFunc = () => {

    urls.forEach(async (url) => {

        //Do not modify this section!
        console.log("a"+url);
        await sleep(5000);
        console.log("b"+url); //Need this to execute before c
    });

};

slowFunc();
console.log("c");

The current output shows "c" being printed before the sleep is complete, which is incorrect. How can I achieve the desired output?

a1
b1
a2
b2
a3
b3
c

Furthermore, out of curiosity, how would I rearrange the output as follows? (The exact order within the 'a' and 'b' sections doesn't matter.)

a1
a2
a3
b1
b2
b3
c

I attempted to study ES2018: asynchronous iteration, but it proved to be quite challenging.

Update: After reconsidering my example, here is a revised version (which still isn't functioning correctly):

var urls = ['https://cdnjs.cloudflare.com/ajax/libs/dompurify/2.2.0/purify.min.js', 'https://cdnjs.cloudflare.com/ajax/libs/systemjs/6.8.3/system.min.js', 'https://cdnjs.cloudflare.com/ajax/libs/slim-select/1.18.6/slimselect.min.js'];
var results = {};

const webRequest = (url) => {
    $.ajax({
        type: "GET",
        url: url,
    }).then(data => {
        results[url] = data;
        console.log("b"+url+","+results[url]); //I want this to run before c
    });
}

const slowFunc = () => {

    urls.forEach((url) => {
        console.log("a"+url);
        webRequest(url);
    });

};

slowFunc();
console.log("c");

Thank you for the feedback so far.

Update 2: Here is a solution to the web request problem, inspired by Antonio Della Fortuna's advice:

var urls = ['https://cdnjs.cloudflare.com/ajax/libs/dompurify/2.2.0/purify.min.js', 'https://cdnjs.cloudflare.com/ajax/libs/systemjs/6.8.3/system.min.js', 'https://cdnjs.cloudflare.com/ajax/libs/slim-select/1.18.6/slimselect.min.js'];
var results = {};

const webRequest = (url) => {
    return new Promise((resolve, reject) => {

        $.ajax({
            type: "GET",
            url: url,
            error: function (data, status, er) { 
                console.log("b,"+url+",failed");
                resolve(); 
            },
        }).then(data => {
            results[url] = data;
            console.log("b,"+url+","+results[url]); //I want this to run before c
            resolve();
        });
    });
}

const slowFunc = async () => {

    for (let i = 0; i < urls.length; i++)
    {
        var url = urls[i];
        console.log("a,"+url);
        await webRequest(url);
    };

};

slowFunc().then(() => {
    console.log("c");
    console.log(results);
})

Answer №1

There are a couple of approaches based on your specific use case, and you can explore a functional example by visiting this link -> https://codesandbox.io/s/zen-carson-ksgzf?file=/src/index.js:569-624:

  1. Concurrent Approach: By executing the requests concurrently within a function, you can output "c" in the following manner:
    const sleep = (delay) => new Promise((resolve) => setTimeout(resolve, delay));
    
    var urls = ["1", "2", "3"];
    
    const slowFunc = async () => {
      await Promise.all(
        urls.map(async (url) => {
          //Do not modify this section!
          console.log("a" + url);
          await sleep(5000);
          console.log("b" + url); //I need this to occur before c
        })
      );
    };
    
    slowFunc().then(() => {
      console.log("c");
    });
  1. Synchronous Approach: Alternatively, you could process all requests as if they were synchronous and wait in sequence:

    const slowFuncSeq = async () => {
      for (const url of urls) {
        //Do not alter this part!
        console.log("a" + url);
        await sleep(5000);
        console.log("b" + url); //I want this to occur before c
      }
    };

    slowFuncSeq().then(() => {
      console.log("c");
    })

Answer №2

When performing asynchronous operations while iterating, the expected outcome may not be as straightforward.

During a forEach loop, each element is processed synchronously, meaning that the callback function is invoked for each element in order. This is why you may notice the 'a' log appearing first for every element.

An exception to this behavior is when using a for...of loop, but in other iterator cases, await will only block within the callback function.

If you need to control the rate of requests made to an API over time, one approach could be implementing a leaky bucket algorithm. Alternatively, you could restructure your iteration process using a for...of loop with delays to regulate request pacing, though this method may not be as efficient due to the delay time plus the duration of completing other asynchronous tasks.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

React Material UI Select component is failing to recognize scrolling event

Having some difficulty understanding how to detect a scroll event with a Select component using Material-UI. The Select has MenuProps={...}, and I want to listen for the scroll event inside it. I've tried putting onScroll within MenuProps={...}, but ...

Increased accuracy in wildcard regexp usage

I wrote a script that uses wildcards to filter filenames. For example, using `*.js` will give me all the JavaScript files in a directory. However, I encountered an issue where it also includes `.json` files in the results, which is not what I intended. T ...

What's the reason for new documents in MongoDB having an object instead of an ObjectId?

When adding new entries into mongodb, the ids no longer appear as ObjectId but instead show up as an object. "_id" : { "_bsontype" : "ObjectID", "id" : "U\u0013[-Ф~\u001d$©t", "generationTime" : 1.43439e+09 } Anticip ...

What is preventing my Button's onClick() method from being effective?

Below is a snippet of my HTML layout using the sciter framework: <div id="volume_micphone_slider" style="z-index:1;"> <input id="volume_slider" class="volume_slider" type="vslider" name="p1c" max="100" value="20" buddy="p1c-buddy" /> < ...

Accessing a .txt file stored in AWS S3 using ReactJS

I've been working on a project in ReactJS where I need to read a text file from an s3 storage. I have the link to the text file stored in an s3 bucket, but I'm having trouble accessing it. Despite my efforts to find a solution online, most resour ...

Where is the function returned by redux-thunk invoked?

Can you help me understand where the function returned by addUser is being called in this action creator and redux thunk function? const userAdded = () => ({ type: types.ADD_USER, }); export const addUser = (user) => { return function (dispat ...

Executing a function inside of JavaScript code

I'm attempting to utilize a function within the "initialize" function of GoogleMaps. Here's what I have: <script> function initialize(){ // initialization parameters function drop(){ //code to drop a marker } } google.maps ...

Angular Directives in Error

Help needed with creating a custom directive in Angular. Seeking guidance :) I am trying to display the content from 'directive.html' within the 'app-info' directive. The code functions properly without the directive, indicating a mist ...

Navigating collisions in the ECS architecture: Best practices

I'm currently developing a game using typescript and the ECS design pattern. One of the challenges I'm facing is handling collisions between different entities within the game world. I have an entity called Player which comprises several componen ...

Exploring React: Leveraging multiple Contexts of the same type to empower children with access to diverse data sources

I have a unique contextual setup that looks something like this const CustomContext = createContext({ info: null }); const getInfo = (key) => { switch(key) { case 1: return "Welcome" case 2: return ...

Exploring the concept of array declaration in JavaScript

I am exploring different ways to create arrays in JavaScript and would like to understand the fundamental differences between two methods. I am also interested in knowing if there is a performance gap between these two "styles" var array_1 = new Array(" ...

Tips for integrating React into a jQuery-centric web application?

After diving into React, I'm eager to showcase my skills by implementing it on select pages as a proof-of-concept for my supervisor at work. Can anyone guide me on how to achieve this using traditional script tags instead of imports? So far, I'v ...

leveraging UI-Router for navigating based on app state and data

Is there a way to dynamically adjust Angular's ui-routing based on certain data conditions? For instance, let's say we need to create a subscription process where the user is informed of whether their subscription was successful or not. As the f ...

Bringing functions from a different module into a Node.js file

When attempting to import a specific function from a folder containing the necessary module, it fails to work. The issue arises when trying to require the email sending function from one module to another within three different folders using nodemailer fo ...

When a user inputs text into a textbox and clicks a link button, it works in Firefox but

There is a textbox on my form with a linkbutton positioned next to it. The ID of the textbox is textbox1 and the linkbutton is lbSearch. In the page_load event, I include the following code: this.TextBox1.Attributes.Add("onkeydown", "if(event.which || ev ...

Enhancing Javascript arrays with powerful functional programming methods

I'm trying to figure out the functionality of this code. Although I am familiar with the reduce and concat functions in Array, I am having trouble grasping how this code operates at first glance: var arrays = [[1, 2, 3], [4, 5], [6]]; console. ...

What is preventing me from creating a perfect circle using the arc() method in Three.js?

While attempting to create a complex shape in Three.js using extruded arcs, I encountered some unexpected behavior. I am unsure if my understanding of the API is lacking, but wasn't this code supposed to generate a full extruded circle with a radius o ...

JavaScript tool for implementing sorting features on an HTML table

I am facing an issue with my AJAX-loaded table where I need sorting functionality implemented. Despite searching for various JavaScript plugins, none seem to work efficiently due to slow performance or errors. The structure of my HTML table is unique with ...

What could be the reason for my dropdown not submitting the form?

Check out my code below: <div class="center"> <p> Choose an item: </p> <div class="dropdown"> @using (Html.BeginForm("Start", "Home", FormMethod.Post, new { name = "form", id = "form" })) { @Html ...

Issue arises when a different method initiates before the completion of the firebase method in ReactJS

Initially, this lambda function operates upon button click. On pressing the button while refreshing the page, an "undefined" message is displayed in the console. However, if I wait for a brief moment without refreshing and then click the button again, the ...