Is there a way to create a recurring Promise .then statement?

Is there a way to create a loop with a repeated .then clause? I need to continue executing the .then clause promise, and if the exit condition is not met, I have to repeat the same process until the condition is satisfied.

My scenario involves making multiple ajax GET requests for different "pages" of data until all the required data is fetched.

To simplify, here is an example of the .then clause that I wish to implement as .thenRepeat:

  .then(function(retData) {
      namesList.push.apply(namesList, retData); // merge data into list
      queryParms.pageNo++;                      // move to next page
      return pretendAjaxFnc(queryParms);        // fetch next page
  })

Here is a runnable demonstration:

function pretendAjaxFnc(obj) {
  return Promise.resolve().then(function() {
    if (obj.pageNo < 6) { // define an imaginary "stop iterating" point.
      // provide some dummy records
      return [{  fld1: "data1",  fld2: "data2" }, 
              {  fld1: "data1",  fld2: "data2" }];
    } else {
      // this serves as the exit condition
      // It will eventually result in a custom exception from a 404 http status
      throw new Error("NO-MORE-DATA");
    }
  });
};

function clientAccumulator() {
  var namesList = [];
  var queryParms = {
    pageNo: 1
  };

  return pretendAjaxFnc(queryParms)
    .then(function(retData) {
      namesList.push.apply(namesList, retData); // add data to list
      queryParms.pageNo++;
      console.log("EIC.GTEST11 list.len: ", namesList.length);
      return pretendAjaxFnc(queryParms);
    })
    // keep repeating until an exit criteria is reached - such as an exception
    .then(function(retData) {
      namesList.push.apply(namesList, retData);
      queryParms.pageNo++;
      console.log("EIC.GTEST21 list.len: ", namesList.length);
      return pretendAjaxFnc(queryParms);
    })
    // keep repeating until an exit criteria is reached - such as an exception
    .then(function(retData) {
      namesList.push.apply(namesList, retData);
      queryParms.pageNo++;
      console.log("EIC.GTEST31 list.len: ", namesList.length);
      return pretendAjaxFnc(queryParms);
    })
    // keep repeating until an exit criteria is reached - such as an exception
    // ...
    .catch(function(ex) {
      if (ex.message === "NO-MORE-DATA") {
        return namesList;
      } else {
        throw ex;
      }
    });
};

clientAccumulator(); // Execute the function

This code is intended for execution on current iOS/Safari & Firefox browsers (with additional variations preferred). AngularJS is used, however, I have attempted to remove any specific references.

I am seeking advice or guidance on implementing a .thenRepeat functionality. Can anyone assist?

Answer №1

To create a recursive function using the then chain, simply wrap it in afunction:

function recurse() {
    return pretendAjaxFnc(queryParams)
      .then(function(retData) {
         namesList.push(...retData);
         queryParms.pageNo++;
         return recurse();
    });
 }

Here is a working example:

function pretendAjaxFnc(obj) {
  return Promise.resolve().then(function() {
    if (obj.pageNo < 6) { 
      return [{  fld1: "data1", fld2: "data2" }, 
              {  fld1: "data1", fld2: "data2" }];
    } else {
      throw new Error("NO-MORE-DATA");
    }
  });
};

function clientAccumulator2() {
  var namesList = [];
  var queryParms = {
    pageNo: 1
  };
  console.log("TEST00 list.len: ", namesList.length);

  function recurse() {
    return pretendAjaxFnc(queryParms)
      .then(function(retData) {
        namesList.push(...retData);
        console.log("TEST01 list.len: ", namesList.length);
        queryParms.pageNo++;
        return recurse();
      });
  }

  return recurse()
    .catch(function(ex) {
      if (ex.message === "NO-MORE-DATA") {
        return namesList;
      } else {
        throw ex;
      }
    });
};
clientAccumulator2(); // Run the function

Answer №2

Recursion can be a valuable tool in programming.

Upon further review, it appears that the following code snippet may align more closely with what you are seeking:

repeatProcessUntil({
    processMethod,
    completionCheckMethod,
    interval = 100
}){
    return processMethod()
        .then((result) => {
            if (completionCheckMethod(result)) {
                return this.$q.resolve(result);
            }

            return this.$timeout(interval)
                .then(() => this.repeatProcessUntil({processMethod, completionCheckMethod, interval}));
        });
}

Below is an example extracted from my own coding practices (adapted to JavaScript for this explanation, hence potential typos)

function retryProcess({
    maxAttempts,
    processMethod,
    successCheckMethod,
    retryInterval,
    incrementInterval = true
}) {
    if (incrementInterval) {
        retryInterval = retryInterval + 1000;
    }

    return processMethod().then((result) => {
        if (successCheckMethod(result)) {
            return $q.resolve(result);
        } else if (maxAttempts > 0) {
            return $timeout(retryInterval)
                .then(() => {
                    return retryProcess({
                        maxAttempts: maxAttempts - 1,
                        processMethod,
                        successCheckMethod,
                        retryInterval,
                        incrementInterval
                    });
                });
        } else {
            retryInterval = 0;
            return $q.reject();
        }
    });
}

Note: I have included some Angular-specifics that I have personally utilized (apologies for overlooking the mention of Angular initially)

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

Exploring the capabilities of Socket.IO in Node.js for establishing a connection with an external server

Background: My localhost (referred to as Server A) hosts a node.js server, while an external server running node.js can be found at (known as Server B). Although I lack control or access over Server B, which serves as a dashboard site for an IoT device in ...

Ways to refresh my $scope once new data is inserted into the SQL database

As I implement the angularjs/SQL technique to fetch data from a database, the code snippet below demonstrates how it is done: $http.get("retrieveData.php").then(function(response){ $scope.tasks = response.data.tasks; }) In addition, there is a functi ...

React Transition Group failing to apply CSS classes

I am utilizing React Transition Group to apply animations with different timeouts to each item. However, the issue arises when my links do not receive any classes. Despite this, wrapping the ScrollLinks tag with TransitionGroup allows the animation to func ...

Guide on setting up a fresh MongoDB database with the Node.js driver

My goal is to establish a new Database in MongoDB by utilizing the Node JS driver. Despite attempting different methods, I encountered a situation where no databases were created (verified through the mongo shell and RoboMongo). Even more frustrating is th ...

The process of extracting data from a form and storing it as global variables

Consider the following example: This is our HTML form <form action="test1" method="GET" name="frm"> First name: <input type="text" name="fname"><br> Last name: <input type="text" name="lname"><br> <i ...

Problem with Ajax functionality on Jquery Dialog

I have implemented a Jquery.dialog feature in my application for sending messages. When the user clicks on "new", the dialog box appears, allowing them to select the recipient (currently working with IDs instead of usernames). On the first opening of the ...

Is there a way to execute a function only once when the submit button is clicked in a jQuery AJAX form?

I am using Django to create a website where users can create groups. To allow users to fill in group information and upload an image as the group logo, I have implemented an HTML form. In order to preview the logo before submission, I have used AJAX to upl ...

Bootstrap Collapse feature fails to collapse on Twitter

I'm having trouble with the bootstrap collapse navbar. It expands fine, but I can't seem to collapse the navigation. Everything seems correct in my code: <nav class="navbar navbar-inverse nav-justified"> <div class="navbar- ...

The hover feature on my website is making the picture flicker

I am experiencing an issue with a map on my website that contains four colored squares. When I hover over one of the squares, the image of the map changes to show the route corresponding to that color. However, this causes the image to shift position and i ...

Developing a personalized logging service in NestJs: capturing logs without directly outputting them

I am currently working on developing a NestJs service that will enhance a Logger. However, I am facing issues with achieving the desired output (e.g., super.warn(); see below for more details). I have followed a tutorial from the nestjs site, but unfortuna ...

Discovering applied styles based on component props in Material-UI v5 using browser developer tools

When working with Material UI v4, I found it easy to identify which styles are applied by component props classname using browser dev tools. This allowed me to override specific styles of the component effortlessly. However, in Material UI v5, I am unsure ...

"Exploring the process of transferring an ID from one service to another using the select feature in Angular

I need to store the ID I receive from one service into another using the select element. Here is my approach: <select class="form-control" id="select_fac" [(ngModel)]="rep.idfac"> <option selected disa ...

Using the value from the Vuex state to set the initial value for a different parameter

I am facing an issue with utilizing an array of objects in my Vuex state to set a default value for another parameter, specifically for mainAccount: For instance: const store = new Vuex.Store({ state: { AccountNums: [ { label: 'M ...

Transforming a string into an object using JavaScript

I am working on the following code snippet: locales = __.each results, (value, index, list) -> locale = value.split("-")[0] # gives me type of string "ru" console.log typeof locale console.log cldr.extractLanguageDisplayNames(locale).ru console ...

Unable to visualize object in three.js

Looking to generate cubes in a random location on the page using a for loop, but currently experiencing issues where the cubes are not appearing as expected. **Note: When I check the cube with console log, this is the output: ** -cube.js:24 Mesh {uuid ...

Determining the typing of a function based on a specific type condition

I have created a unique type structure as shown below: type Criteria = 'Criterion A' | 'Criterion B'; type NoCriteria = 'NO CRITERIA'; type Props = { label?: string; required?: boolean; disabled?: boolean; } & ( | ...

A guide to setting a custom icon for the DatePicker component in Material-UI 5

Seeking to incorporate custom Icons from react-feathers, I have implemented a CustomIcon component which returns the desired icon based on the name prop. Below is the code for this component. import React from 'react'; import * as Icon from &apo ...

I am seeking guidance on implementing a 301 redirect within a Node JS project using an Express JS router. Can

Currently facing an issue with setting up a 301 redirect from 'domain.com' to 'www.domain.com' on my project hosted on Heroku Cloud. I have tried using Topic Search and Google for solutions, as well as other search engines, but haven&ap ...

Conducting a directory verification process using Node.js

Is there a method to execute a server script that verifies the presence of all necessary directories in the server directory? I have explored using server.on('event') but it appears that this specific event does not exist. ...

Transforming text into visual content using a live preview div powered by jQuery

Looking for a way to display images in real-time as a user types letters into a textarea field. When a user inputs a letter like 'k', an image associated with that letter should appear. Currently, only pre-entered letters on the page show images, ...