What is the step-by-step process for chaining ajax requests using $q.deffer?

I have a task that requires the browser to make N requests to the server, where the requests must be synchronous and start only after the previous request has completed.

One way to achieve this is by writing a function with a for loop and recursively calling it, but it can lead to callback hell and not very elegant. I am looking for a more elegant solution.

I came across deferred objects which some say can help avoid callback hell. I want something similar to using setTimeout to simulate an asynchronous request:

    function foo1(some) {
        debugger;
        setTimeout(function foo1async() {
            debugger;
            deffered.resolve();
        }, 500);
        return deffered.promise;
    }

    function foo2(some) {
        debugger;
        setTimeout(function foo2async() {
            debugger;
            deffered.reject();
        }, 500);
        return deffered.promise;
    }

    function foo3() {
        debugger;
        setTimeout(function foo3async() {
            debugger;
            deffered.resolve();
        }, 500);
        return deffered.promise;
    }

    var deffered;
    function doChain() {
        debugger;
        deffered = $q.defer();
        var promise = deffered.promise;
        promise.then(foo1);
        promise.then(foo2);
        promise.then(foo3);
        promise["finally"](function () {
            debugger;
        });
        deffered.resolve();
    }
  1. I expect foo1 to be called, then foo1async will be called and resolve the deferred object.
  2. foo2 should be called next, followed by foo2async. 3.Now, I expect that foo3 wouldn't be executed because the deferred is rejected in foo2async. After that, I expect the code in the finally section to be called.

However, what actually happens is that foo1, foo2, and foo3 are all called. Then the code in the finally section runs. Finally, foo1async, foo2async, and foo3async functions are called.

I want to achieve my expected behavior by implementing something like this:

for(var i = 0; i < N; i++) {
    (function (iter) {
        promise.then(function () {
            foo(iter);
        });
    })(i);
}

Answer №1

It seems there are a few misconceptions in your approach.

Firstly, the use of deferred to convert a callback-based async function into a promise-based one requires each function to have its own deferred.promise and deferred. I find using the $q constructor a more straightforward alternative:

function fooN(input){
  return $q(function(resolve, reject){
    setTimeout(function(){
      resolve(input + "; some additional data");
    }, 500);
  });
}

(You could also utilize var deferred = $q.defer())

With this implementation, fooN now directly returns a promise without the need for $q.defer().

If the async function is already promise-based like $timeout or $http, then using deferred is unnecessary as shown below:

function fooN(input){
  return $timeout(function(){
    return input + "; some additional data";
  }, 500);
})

Assuming that foo1, foo2, and foo3 all follow the structure of fooN by returning promises.

To ensure sequential execution, chaining promises is required rather than attaching multiple handlers to a root promise.

Here is a breakdown:

function doChain(){
  var foo1Promise = foo1();
  var foo2AfterFoo1Promise = foo1Promise.then(foo2);
  var foo3AfterFoo2Promise = foo2AfterFoo1Promise.then(foo3);

  var promise = foo3AfterFoo2Promise.then(function(finalData){
    return doSomeProcessing(finalData); // if necessary
  });

  promise.catch(function(error){
    // "rethrow" error if unable to handle
    return $q.reject({msg: "An error occurred"});
  })

  return promise;
}

Alternatively, a more concise version:

function doChain(p){
  return foo1(p)
          .then(foo2)
          .then(foo3)
          .then(function(finalData){
            return doSomeProcessing(finalData);
          })
          .catch(function(error){
            return $q.reject({msg: "An error occurred"});
          });
}

The returned promise of each function serves as input to the subsequent chained function.

Answer №2

One possible solution is to utilize the $q.all method in AngularJs like this:

var promisesToResolve = [promise1, promise2, ...];

$q.all(promisesToResolve).then(function () {
// perform actions here
});

Answer №3

What's happening now is that all foo* promises are relying on a single promise; once it's resolved, they are all activated. In visual form, the dependencies look like this:

        ┎ foo1
promise ╁ foo2
        ┖ foo3

What you really need is:

function chainPromises() {
    foo1()
        .then(foo2)
        .then(foo3)
    ;
}

No extra promise necessary. Say goodbye to callback hell!

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

What is the best way to transfer the $http response value to another function?

I currently have these two functions set up. One function, $scope.submit, handles posting data to the server and capturing the response value. The other function, $scope.addTeams, is responsible for adding teams based on the response from $scope.submit. ...

Tips on retaining the value of $index in ng-repeat and storing it within the array

I currently have a cart for shopping. The code for the "add to cart" function looks something like this (shortened): "add" : function(code) { codes.push({ "id" : code.id, "order" : "N/A", ...

Angular: Mastering the art of filtering within nested ng-repeats

Check out my website At the moment, all the data is stored in a single array like this (which currently works fine): $scope.data = [ { category: 'FundRacers', title: 'FundRacers Events', link: 'https ...

Using jQuery's $.Deferred in conjunction with the window object's top.postMessage()

I am having trouble understanding how to effectively use $.Deferred. I currently have a situation similar to window.top.postMessage(mystring, myorigin); This works without any issues. I don't need assistance with sending/receiving postMessage What ...

Is it possible for me to move props object deconstruction into a separate module?

Here is my scenario: I have two React components that share 90% of the same props data, but display different HTML structures. I would like to avoid duplicating variable declarations in both component files. Is there a way to extract the common props des ...

Navigating through states in AngularJS

My application has three states: app, app.devices, and app.devices.device. While the first two states are functioning properly, the app.devices.device state is not working as expected. Here is the code for reference: http://pastebin.com/r1kYuExp http://pa ...

Is there a way to determine the quantity of lines within a div using a Vue3 watcher?

Is it feasible to determine the number of text lines in a div without line breaks? I am looking to dynamically display or hide my CTA link based on whether the text is less than or equal to the -webkit-line-clamp value: SCRIPT: const isExpanded = ref(true ...

Setting up Jest

I'm currently attempting to integrate the Jest Testing framework into my React Native project. Unfortunately, I am encountering an error message: Failed to retrieve mock metadata: /Users/me/Documents/Development/project/node_modules/global/window.js ...

Move the cursor within the text area upon clicking the button

When the "+header" button is clicked, I am looking to automatically position the insertion point inside the text area. Currently, after pressing the button, the text box displays information like address, date, time etc. but the cursor does not start insid ...

Error in Redux reducer file caused by an incorrect data type in action.payload

I have encountered a type error in my reducers' file where it says that my 'Property 'address' does not exist on type 'number | { connection: boolean; address: string; }'. This issue is arising in my React application while us ...

Achieving the perfect alignment: Centering a paragraph containing an image using JQuery

I need help centering the background image of my <p> tag on the webpage. Script $(function() { $('ul.nav a').bind('click', function(event) { var $anchor = $(this); $('html, body').stop().animate({ ...

Utilizing Vue.js 2.x to send a REST API request with a collection of objects

I currently have an array of objects stored in state, and my goal is to send this entire structure to a back end API for processing and receive a new set of values in return. Below is a simplified representation of this structure as viewed in the develope ...

During the click event, two distinct $.ajax requests interfere and cancel each other out

Here's a dilemma I'm facing: On my webpage, I have implemented two different click events. The first one opens a modal displaying a larger image when you click on a thumbnail picture (similar to Instagram on PC - I created an Instagram clone for ...

AngularJS filtering displays only the matched word as the result, rather than the entire string

Is there a way to only display the matched word in ng-repeat using filters? Let's say we have the following strings [ {text: 'This is first text from the array.'}, {text: 'Text in this array is dummy and it makes no sense'}, {te ...

Looking for a quick guide on creating a basic RESTful service using Express.js, Node.js, and Mongoose?

As a newcomer to nodejs and mongoDB, I've been searching high and low on the internet for a tutorial that combines express with node and mongoose. What I'm specifically looking for is how to use express's route feature to handle requests and ...

Tips for updating a URL using data obtained from a JSON response

As I loop through an array from JSON, I extract all the necessary information. For example, if I have cards for blog posts that include the title, short description, published date, and URL. When clicking on a card, it redirects to a corresponding page ba ...

Steer clear of retrieving all the elements from the HTML DOM at once

Scenario I am working on a HTML5+CSS+JS slideshow project that needs to be synchronized across 50 clients in a local area network using a single wireless router. Challenge Due to the heavy content, particularly images, of the slides, I intend to dynamic ...

Guide on transforming a select dropdown into a ul dropdown

I am attempting to transform my select dropdown menu into an unordered list (ul) in order to create a bootstrap button that will display the ul, allowing the list items to function as options. <select id="sortField" onchange="refreshPage('{$pageBa ...

AgGrid Encounters Difficulty in Recovering Original Grid Information

After making an initial API call, I populate the grid with data. One of the fields that is editable is the Price cell. If I edit a Price cell and then click the Restore button, the original dataset is restored. However, if I edit a Price cell again, the ...

How to declare WinJS.xhr as a global variable in a Windows 8 Metro app

Currently, I am developing a Windows 8 Metro app and facing an issue with a hub page. The problem arises when pushing data dynamically from an XML API using two WinJs.xhr("url") calls; one for headings and the other for section datas. When merging both W ...