Angular triggers a function upon completion of several API requests

I am currently working on an Angular service that involves making HTTP calls. Here is an overview of the code structure:

this.checkAndSendNotifications = function() {
    UsersService.getArray(function(array) {
        var notifications = [];
        angular.forEach(array, function(element) {
            if (some conditions are met) {
                srv.sendNotificationToToken(element.id,
                    function() {
                        notifications.push({
                            id: user.id,
                            errorStatus: null,
                            errorStatusText: null
                        });
                    },
                    function(error) {
                        notifications.push({
                            id: user.id,
                            errorStatus: error.status,
                            errorStatusText: error.statusText
                        });
                    });
            }
        });
        printNotificationsStatus(notifications);
    });
};

this.sendNotificationToToken = function(id, onSuccess, onError) {
    $http({
        method: 'POST',
        url: 'https://url....',
        headers: {
            'Authorization': 'Bearer ....',
            'Content-Type': 'application/json'
        },
        data: {
            "id": id,
            "message": "hello"
        }
    }).then(function successCallback(response) {
        onSuccess();
    }, function errorCallback(error) {
        onError(error)
    });
};

I am facing a challenge where I need to ensure that the printNotificationsStatus() function is called only after all API calls are completed to guarantee that all API responses are received. Currently, the function is being called at the end of the angular.forEach execution, which could lead to asynchronous API promises being resolved later than expected.

Is there a solution to synchronize the API calls?

Thank you in advance, Davide

Answer №1

If you want to ensure all promises are resolved before proceeding, you can utilize $q.all. Here's an example implementation:

UsersService.getArray(function(array) {

    var promises = [];
    var notifications = [];
    angular.forEach(array, function(element) {

        if (some conditions are met) {
            var promise = srv.sendNotificationToToken(element.id,
                function() {
                    notifications.push({
                        id: user.id,
                        errorStatus: null,
                        errorStatusText: null
                    });
                },
                function(error) {
                    notifications.push({
                        id: user.id,
                        errorStatus: error.status,
                        errorStatusText: error.statusText
                    });
                });
            promises.push(promise);
        }

    });

    // Wait for all promises to resolve
    $q.all(promises).then(function () {
        printNotificationsStatus(notifications);    
    })

 });
};

Remember to inject $q into your code.

Ps: You should be careful not to execute printNotificationsStatus() after the first iteration in your code.

Answer №2

In analyzing your code, it first catches my attention that the printNotificationsStatus function is called within the forEach loop. This means it will be executed for each item in the array. If this was a synchronous process, it could have been placed outside the loop.

However, since you are making an asynchronous call within the forEach loop, the main thread will not wait for the responses from sendNotificationToToken.

There are several effective patterns to handle this kind of situation in JavaScript. One common approach, as suggested by @Bruno Pares, can be found here:

Using libraries like Async can also be a good option.

With the callbacks you are currently utilizing, you can refactor the code to keep track of the iteration process. Once all iterations are complete, you can then call the printNotificationsStatus function. The answer provided in the following link seems to address your specific query quite well:

function callback () { console.log('all done'); }

var itemsProcessed = 0;

[1, 2, 3].forEach((item, index, array) => {
  asyncFunction(item, () => {
    itemsProcessed++;
    if(itemsProcessed === array.length) {
      callback();
    }
  });
});

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

Using jQuery to retrieve the content of a textarea and display it

I need help finding the right way to read and write to a Linux text file using JavaScript, jQuery, and PHP. Specifically, I want to retrieve the value from a textarea (#taFile) with jQuery ($("#taFile").val();) and send it via $.post to a PHP script that w ...

Tips for dynamically updating data with on.click functions in D3

Implementing pack layout with D3 v4 is my current task, and I am looking to adjust the circle sizes based on the values "funds" and "spend" in my csv file. The following code successfully scales the circles: rank(funds) rank(spend) However, the on.click ...

Issue with AJAX POST request: PHP failing to establish session

I would like to pass the element's id to PHP and create a session for it. This snippet is from a PHP file: <?php $sql = "SELECT id FROM products"; $result = mysqli_query($con,$sql); while($row = mysqli_fetch_array($result)) { ?> <tr cl ...

Manipulating a 2D array in Javascript and Jquery: Implementing push functionality

I am trying to set up an array structure as follows: track[divID][wrapID] However, when I attempted track[divID][wrapID] = wrapID, I encountered an issue. This is because I need to add more elements to the second dimension in another loop, like this: tr ...

Tips for transferring data between two forms in separate iFrames

I'm trying to achieve a functionality where the data entered in one form can be submitted to another form within an iframe. The idea is to allow visitors to preview their selected car in the iframe, and if satisfied, simply press save on the first for ...

I need to transfer the "message" variable from outside to inside the res.json function

Access Control page: https://i.stack.imgur.com/oUSEB.png While working with the 'passport.use' function, I have a message variable that needs to be passed into the 'passport.authenticate' function so it can be utilized in the contro ...

Creating recursive functions to produce a unique variable without any repetitions

Currently utilizing MongoDB and Express in my project. I have a post endpoint that generates a random name for the name field. Everything is working as expected except when I encounter a duplicate name. I need to check if the name already exists in the d ...

Sending a request from JavaScript to C# methods using AJAX, with no expected response, within an ASP.NET MVC framework

Setting up the Environment: <package id="jQuery" version="3.2.1" targetFramework="net45" /> <package id="Microsoft.AspNet.Mvc" version="5.2.3" targetFramework="net45" /> Recently, I encountered an issue while trying to send a request from one ...

The method mongoose.connect() is not defined

Having a bit of trouble connecting to my MongoDB using Mongoose - keep getting this error. const { mongoose } = require('mongoose'); const db = 'dburl.com/db' mongoose.connect(db, { useNewUrlParser: true }) .then(() => console ...

Tips for calculating the distance from the cursor position to the visible area

Is there a way to determine the cursor's offset from the top of a textarea's view rather than its position? While e.target.selectionStart provides the cursor position, $el.scrollTop gives the scroll offset of the textarea. Any suggestions on ho ...

The issue of the Ajax beforeSend function not triggering at times, causing a delay in displaying the bootstrap progress

I'm experiencing issues with my Bootstrap tabs and the progress bar not consistently showing up. I have 3 tabs, each displaying query results in a table. Whenever the search button is clicked or a tab is changed, an ajax call triggers a function with ...

Laravel: The current configuration does not support the experimental syntax 'classProperties' at this time

When compiling my javascript files using npm run dev, I encountered a warning in my resource/app.js file where I require my custom validation script. The warning stated the following: Module build failed (from ./node_modules/babel-loader/lib/index.js): Syn ...

Having trouble setting the select value with JavaScript in the Selenium web driver

I am working on a web page that includes a cascaded dropdown feature. The data in the second dropdown appears to be generated via ajax based on the selection made in the first dropdown. Here is the code for the first select option: <select class="form- ...

The functionality of Jquery .slideToggle("slow") seems to be glitchy when used as a table expander

I attempted to implement the .slideToggle("slow"); feature to my table, following the instructions detailed here: W3Schools The toggle effect seems to be functioning, but not as expected. I want the effect to behave similar to the example on the W3School ...

The <base> tag functions properly when used with the <a href> attribute, but encounters issues when used with JavaScript's document

Encountering an issue with the <base> tag in Internet Explorer (tested on IE11). Despite successfully getting my links to work using the <a href> tag, they fail to work when attempting to use JS document.location. To see this issue in action, ...

Merging object keys and values from JSON arrays based on their keys, using JavaScript

Is there a way to merge object keys' values from JSON arrays based on their key? json1 = [ {key:'xyz', value:['a','b']}, {key:'pqrs', value:['x','y']} ] json2 = ...

The jQuery .val(#) function does not update the selection after an AJAX call, but it functions correctly when debugging or paused

Currently encountering an interesting issue - after executing an AJAX call that updates the Appointment List dropdown with new entries based on page load or a user-entered filter, the .val() method is failing to set the value correctly. The successful exe ...

Inquiries about ngshow and the scope concept

I have a question about using AngularJS. I have multiple sections and only want to display one at a time using <section ng-show="section6_us"> </section> and <section ng-show="section7_us"> </section>. My scope has many variables. ...

Breaking down a javascript project

As the trend of splitting large JavaScript projects into separate files and then compiling them into a single distribution increases, I am eager to explore this workflow. While I have considered Node.js, npm, and Grunt for this purpose, I find the learning ...

Discovering a specific element within a deeply nested JavaScript object

let data = [{ "ItemAID" : 1, "ItemADesc" : [ { "ItemBid" : 11, "ItemBDesc" : [ { "ItemCid" : 111, "ItemCTitle" : "TitleC111", }, { " ...