Angular Cascade of Multiple Requests

I am currently working on a project where I need to develop a service that can take CSV data, convert it into rows, and then make API requests for each row, adding the formatted results to an output string.

To explain further, my code (written in Coffeescript) looks something like this:

  $s.batchFetch = ->
    return unless $s.csv
    $s.output = ''
    for row in $s.csv.split("\n")
      $s._addToOutput(row)

The $s._addToOutput() function is responsible for making an API call with each row, formatting the response, and appending it to the output string ($s.output). Here is a simplified version of how it works:

$s._addToOutput (row) = -> 
  formattedResponse = ''
  $http.get("api/request/path/row-specific-whatever")
    .success (res) ->
      formattedResponse = $s._format(res)
    .then ->
      $s.output += formattedResponse

The issue at hand is that the order of the responses added to the output string seems random. It appears that the speed of the API responses varies, causing responses to be added out of order based on when they are received -- rather than following the original order of the rows.

I believe that using Angular promise chaining could potentially solve this problem, as shown here:

$s._addToOutput(row).then ->
  $s._addToOutput(secondRow).then ->
    $s._addToOutput(thirdRow).then ->
      ...

However, since the number of incoming rows is unpredictable, I would like a more dynamic solution that allows me to perform API calls for each row sequentially.

If anyone has any suggestions on how to achieve this, I would greatly appreciate the assistance. Thank you!

Sasha

EDIT -- I attempted ryeballar's solution, but unfortunately, it did not resolve the reordering issue. I suspect there may be an error in my implementation, so if anyone notices anything amiss, please let me know:

(Please note, I had to modify the solution slightly due to making two consecutive requests for each row: one for the "venue" and another for the venue's "photo." Also, yamlify essentially means 'format'.)

...

Answer №1

To utilize the .reduce() method, you can call each promise sequentially. Begin with an initial promise that resolves immediately using $q.when(), then proceed to invoke the next promise rowPromise, creating a chain effect when each subsequent then() is triggered.

.controller('CsvController', function($scope, $q) {

  $scope.csv = '.....';
  $scope._format = function() {/*...*/};

  $scope.batchFetch = function() {
    $scope.output = '';
    return $scope.csv.split('\n').reduce(function(promise, row) {
       return promise.then(function() {
          return $http.get("api/request/path/row-specific-whatever", {row: row})
                          .success(function(res) {
                             $scope.output += $scope._format(res);
                          });
       });
    }, $q.when());
  };

});

UPDATE: Upon reviewing the code, it appears that the current implementation leads to a callback hell issue. Thus, restructuring the code in the following manner may provide a better solution:

$s.batchFetch = function() {
  if (!$s.csv) {
    return;
  }
  $s.output = '';
  return $s.csv.split("\n").reduce(function(promise, row) {
    return promise.then(function() {
       var split = row.split(',');
       return $s._getVenue(split[0], split[1]).success(function(res) {
         var venue = res.response.groups[0].items[0].venue;
         return $s._getPhoto(venue).success(function(resp) {
           var photo = $s._photoUrl(resp);
           return $s.output += $s._yamlify(venue, row, photo);
         });
       });
    });
  }, $q.when());
};

Alternatively, consider structuring the function as follows to avoid cascading callbacks:

$s.batchFetch = function() {
  if (!$s.csv) {
    return;
  }
  $s.output = '';
  return $s.csv.split("\n").reduce(function(promise, row) {
    var split, venue, photo;
    return promise
       .then(function() {
          split = row.split(',');
          return $s._getVenue(split[0], split[1]);
       }).then(function(response) {
          venue = response.data.response.groups[0].items[0].venue;
          return $s._getPhoto(venue);
       }).then(function(response) {
          photo = $s._photoUrl(response.data);
          return $s.output += $s._yamlify(venue, row, photo);
       });
  }, $q.when());
};

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

Acquiring JSON data within a JavaScript function using PHP

Requesting assistance on reading a JSON value using the code below, to retrieve data based on the $movieID. <script> function bookBTN(x) { <?php $url = 'http://movie.com/movieOne?seatNum=' . x; $jsonURL = file_get_ ...

When scrolling, a new page loads seamlessly

Recently, I came across a website that has an interesting feature where new content is loaded automatically while scrolling, seamlessly appending to the existing page. What's more fascinating is that not only does the content change, but the URL also ...

Passing Headers from NodeJS to Angular

Running a single-page Angular web app with a Node server, requests to the server are received from a reverse proxy that includes authentication headers. The Angular app occasionally sends requests to another server that require the same authentication he ...

Comparing Two Arrays in AngularJS and Disabling on Identical Cases

I currently have two tables: "Available subject" and "Added subject" which are populated by ng-repeat with two separate lists of objects. My objective is to accomplish three things: 1 - When the user clicks on the plus sign, a row with identical content s ...

How is it possible for this for loop to function properly without the need to pass the incrementing variable

I managed to compile my code and it's working fine, but there's something interesting - the variable that should reference the incrementing value is not included as an argument in the for loop. var _loop2 = function _loop2() { var p = docume ...

Problem Encountered with Navigation Bar following the Cloning and File Transfer in a React (Next) Project

I cloned the repository and transferred the files using these commands: 1. git clone https://github.com/adrianhajdin/portfolio.git 2. cd portfolio 3. mv * .. Upon executing npm run dev, everything appears fine, but upon clicking on the About button or ...

Error callback not being invoked on save() method in AngularJS service

So I am currently using the AngularJS Restful Service $resource in my project. However, when I try to call the $save function and provide an error callback, it does not get invoked. Surprisingly, even though the server sends a 418 error, which is not a suc ...

Displaying AJAX Confirmation in a Popup Window

My form is set up to insert data into a database via an AJAX request. Upon successful insertion, the "rentinsert.php" will display a success message on the page that reads "Your order has been placed". However, I would like this success message to appear i ...

There is an error in ReactJS: TypeError - _this.props.match is not defined

I am experiencing a TypeError in my console tab and I can't seem to figure out where the error is occurring in my source code. I am relatively new to ReactJS so any help in identifying what I'm doing wrong would be greatly appreciated. Thank you ...

When working with Node.js, it is important to properly export your Oracle database connection to avoid encountering an error like "TypeError: Cannot read property 'execute

Hey there, I am a newbie when it comes to Node.js and Oracle. I've managed to create an app and establish a successful connection to the database. Now, I'm trying to figure out how to utilize the connection object throughout my application. Any s ...

Tips for utilizing a printer to print HTML content in PHP

I've stored HTML content in a variable as shown below: $message ="<div> <table align='center'> <tr><td><h1>Tipo de Documentos Report</h1></td></tr> <tr><td>< ...

The edges of the cubemap in THREE.js appear murky

I am attempting to create a black skybox with white dots (stars) in three.js. However, due to the perspective effect, the dots appear darker in the corners where they are further away (as they get smaller and dimmer). Is there a way to make the appearance ...

JavaScript utilizing an API to retrieve specific data values

Below is some API Data that I have: [ { "symbol": "AAPL", "name": "Apple Inc.", "price": 144.98, "changesPercentage": -1.22, "change": -1.79, ...

What is the best way to incorporate messages across various channels with a Discord bot?

While browsing through the questions of other users, I stumbled upon exactly what I was looking for. My dilemma now is how to make the same bot perform similar tasks on various channels but with different titles, footers, and so on... The primary code sni ...

Arranging a list without using the ng-repeat directive

Due to specific constraints, I have opted not to utilize ng-repeat for a list of items. Here is the structure of my list: <p>Search: <input type="text" ng-model="search"></p> <div id="grid" filter-list="search"> &l ...

Issue: The object identified as #<Object> does not contain the 'toUpperCase' method

I am currently encountering the error Object #<Object> has no method 'toUpperCase' right before the POST request is initiated. Any assistance you can provide would be greatly appreciated! Thank you in advance. function ajaxeo(url, data, me ...

"Trouble arises when dealing with nested object arrays in Formik and handling changes in a child

I am struggling with passing a value to handleChange in formik validation. To address this issue, I created a component whose quantity is dynamically added based on the number of numChild. My goal is to allow users to click an icon and add any number of sk ...

incorporating a timer into a JavaScript game

I am currently working on a memory card game where I want to include a stopwatch feature. I'm looking to display the time elapsed from when the user selects the first card until they win. However, I am struggling with keeping the stopwatch running smo ...

What could be causing my component to not update after I modify the states?

When I make an ajax request and update the state with the response data, my list of items does not rerender as expected. Even though the state is updated successfully, the changes are not reflected in the UI. export class Tiles extends React.Component { ...

PHP: Communicating Data with JavaScript

What if my PHP script requires some time to complete its operations? How can I keep the client updated on the progress of the operation, such as during a file download where the estimated time and data size need to be communicated? In PHP, calculating all ...