Using AngularJS to handle promises within a recursive function

I have been attempting to implement the AngularJS promise/then functionality with a recursive function, but I am facing an issue where the then-function is not being called. None of the error-, success-, or notify-callbacks are getting triggered.

Below is the code snippet:

Recursive Function

loadSection2 = function() {

    var apiURL = "http://..."

    var deferred = $q.defer();

    $http({
        method: "GET",
        url: apiURL
    }).success(function(result, status, headers, config) {
        console.log(result);
        loadCount++;
        if(loadCount < 10) {
            newSectionArray.push(result);
            loadSection2(); 
        } else {
            loadCount = 0;
            deferred.resolve();
            return deferred.promise;
        }
    }).error(function() {
        return deferred.reject();
    });
    deferred.notify();
    return deferred.promise;
};

Then-Function

loadSection2().then(function() {
    console.log("NEW SECTIONS LOADED, start adding to document");
    addContent();
}, function() {
    console.log("ERROR CALLBACK");
}, function() {
    console.log("NOTIFY CALLBACK");
}).then(function() {
    loadScrollActive = false;
});

It seems like the then should trigger the initial notify-callback at least. However, no callbacks are being invoked. Could it be that then does not work well with recursive functions?

Answer №1

UPDATE - 11/11/2015 For those who are not concerned about notifications, here is a more streamlined approach:

loadSection2 = function (){
    var apiURL = "http://..."
    return $http.get(apiURL)
        .then(function(response){
            loadCount++;        
            if (loadCount < 10) {
                newSectionArray.push(response.data);
                return loadSection2();
            }
            loadCount = 0;
        });

};

Previous response can be found below:

You have the option to continuously pass the promise throughout.

loadSection2 = function(deferred) {

    if(!deferred){
        deferred = $q.defer();
    }
    var apiURL = "http://..."

    $http({
        method: "GET",
        url: apiURL
    }).success(function(result, status, headers, config) {
        console.log(result);
        loadCount++;
        if(loadCount < 10) {
            newSectionArray.push(result);
            loadSection2(deferred); 
        } else {
            loadCount = 0;
            deferred.resolve();
            return deferred.promise;
        }
    }).error(function() {
        return deferred.reject();
    });
    deferred.notify();
    return deferred.promise;
};

Answer №2

I came up with a unique solution that avoids passing the "deferred" variable around. While it may not be considered the best approach, it is functional and taught me something new (jsfiddle).

27/Jul/15 - I have updated the code to be more concise by eliminating the need to create another promise in f1(). Hopefully, the connection to the original question is now clearer. If not, feel free to leave a comment for clarification.

f1().then(function() {
    console.log("task complete");
});

function f1(counter) {

    if (!counter) {
        counter = 0;
    }

    counter++;
    console.log(counter);

    return asyncTask().then(function() {
        if (counter < 10) {
            return f1(counter);
        } else {
            return;
        }
    });

}

function asyncTask() {
    var deferredTask = $q.defer();

    $timeout(function() {
        deferredTask.resolve();
    }, 100);

    return deferredTask.promise;
}

Answer №3

Fauphi,

Using recursion is a valid option, although it may not be the most promising approach.

If you have deferreds/promises at your disposal, you can dynamically construct a .then() chain to generate a promise of an array with data.

function loadSection2(arr) {
    return $http({
        method: "GET",
        url: "http://..."
    }).then(function(result, status, headers, config) {
        console.log(result);
        arr.push(result);
        return arr;
    }, function() {
        console.log("GET error");
        return $q.defer().resolve(arr).promise;
        //Alternatively, using $q's .then() simplifies this as...
        //return arr;
    });
};

var newSectionPromise = $q.defer().resolve([]);

for (var i=0; i<10; i++) {
    newSectionPromise = newSectionPromise.then(loadSection2);
}

newSectionPromise().then(function(arr) {
    console.log(arr.length + " new sections loaded. Starting to add them to the document.");
    addContent(arr);
}, function() {
    console.log("ERROR CALLBACK");
}).then(function() {
    loadScrollActive = false;
});

Note: This code is untested.

The previous newSectionArray variable is now created anonymously and passed through the .then() chain without depending on individual GET success or failure. It will appear as arr in the final .then's success handler, where it is then used by addContent(). In doing so, we eliminate the need for maintaining a separate member like newSectionArray in the outer scope.

To further reduce external members, loadSection2 could also be made anonymous.

The explicit notification requirement decreases since:

  • there is no longer a main deferred object to notify
  • console.log(result); within the GET success handler provides sufficient notification.

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

The color of the three js cube is charcoal, definitely not a fiery red

Just dipping my toes into the world of three.js...my cube is displaying in black even though I set the color to red. Any ideas why? <!DOCTYPE html> <html> <head> <title>Experimenting with shapes</title> & ...

How to retrieve the total count of fields in AngularJS

Could you please specify the number of choices you would like to display? For example, if the user enters 10, then 10 choices will be displayed. I have been trying to implement this functionality, but it keeps resulting in a memory error. dynamicform.jsp ...

Executing an HTML webpage with npm package.json file

I have recently discovered package.json files and am new to using them. I have created an HTML webpage using three files: HTML, CSS, and JavaScript. Currently, I open the HTML file directly in my browser but have been advised to use a package.json file a ...

Tips for managing Material-ui's <Autocomplete/> component based on the option's id

When dealing with HTML select in React, it's common to use an id or key to keep track of the selected value: <select value={value} onChange={(event) => setValue(event.target.value)}> {options.map((option) => ( <option value={optio ...

There is no index signature in AxiosStatic

As I convert a hook from JavaScript to TypeScript, I encounter the following error: (alias) const axios: AxiosStatic import axios Element implicitly has an 'any' type because type 'AxiosStatic' has no index signature. Did you mean to ca ...

Accessing index.html via file:// from Vue-cli template

Whenever I execute the npm run build command using this Vue-cli template, it displays this message: Hint: The built files are designed to be served over an HTTP server. Attempting to open index.html via file:// will not function correctly. Therefore, the ...

Is there a way to incorporate locales in calculations involving percentages?

Given the number 4030.146852312 I want to retrieve the four decimal places from this number, resulting in: 4030.1468 Furthermore, I need to format this number according to the specified locale. For example: 4.030,1468 What is the best way to achieve thi ...

How to utilize an AngularJS filter within a controller

Having a root controller in my AngularJS web application with a filter. The filter functions properly when applied in the html template, but it fails to work when attempted to be applied in the controller. function Controller ( ... deps ...) { filter ...

Exploring byte array manipulation in node.js and techniques for data processing

Currently, I am faced with the challenge of retrieving a full byte array from a socket and then inserting it into a BLOB database without formatting the data. This is necessary as I specifically need to maintain the structure of the byte array. Initially, ...

Tips for managing JavaScript errors

<ItemTemplate> <tr class="row"> <td style="width: 88%;"> <input id="hdnPackageProuctId" type="hidden" value='<%# Eval("ID")%>' /> <div style="float:left; margin-top:5px;height: ...

Virtual machines have encountered issues when attempting to utilize setTimeout within the browser with vm.runInNewContext

When running a JS script using the vm module in a browser, the following details are included: vm.runInNewContext(codeToEval, sandboxObject); Although interval methods like setTimeout and setInterval do not work, even when exposed in the sandboxObject cr ...

Basic HTML Audio Player Featuring Several Customizable Variables

I have a unique API that manages music playback. Instead of playing audio in the browser, it is done through a Discord bot. Achievement Goal https://i.stack.imgur.com/w3WUJ.png Parameters: current: indicates the current position of the track (e.g. 2:3 ...

jQuery allows you to disable all other checkboxes when a specific one is checked

Whenever the "No" option is selected from a group of radio buttons, a DIV containing 6 checkboxes (with different names/IDs) should be revealed. Likewise, if the "None" option is checked, the other checkboxes should be disabled (and enabled once "None" is ...

The function defineCall does not exist, causing a sequelize error to occur

A challenge I've been facing is resolving an error that is popping up in my index.js file. I'm currently utilizing Node, Express, and sequelize in my project. /app/node_modules/sequelize/lib/sequelize.js:691 server_1 | this.importCache ...

How can I perform a string search that doesn't take into account accented characters

Looking for a way to treat accented characters as equivalent to their non-accented counterparts in my code. Here's what I have so far: var re = new RegExp(string, 'i'); if(target.search(re) == 0) { } Currently, the code ignores the charact ...

``To utilize the ng-class variable within a controller, one can simply assign the desired

How can I change the color of a div to green using AngularJs when a file is selected? html <div class="text-arrow" ng-class="{activeBackground:applyActive}">File Selection<span class="arrow-div"></span></div> css .activeBack ...

Load as soon as the browser is launched

I have developed a unique button that utilizes JavaScript to display the server status through an API. However, I am facing an issue where the server status does not automatically load when the browser is opened for the first time. You need to manually cli ...

Add multiple controls to a div element

Working efficiently, I've set up a system to collect data within a div by inserting hidden input boxes before submitting it to the server. Below is the JavaScript code I'm using: function appendToDiv() { var mydiv=document.getElementById("somed ...

Tips for displaying HTML5 validation messages when the input is changed

I am working on a form where I prefer not to submit it directly, as I am making an AJAX call instead. However, I still want the HTML5 validation messages to show up when an input field is left empty. I have managed to display the validation message when a ...

Interested in gaining knowledge on how to define basic properties on the model within Angular.JS?

As I dive into the demos on the angular.js website, one aspect that caught my attention is the lack of explicit model/properties definition compared to other frameworks like durandal. For instance, how can we access the property "yourName" in a particular ...