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

Display HTML components as JSON data using AngularJS

Recently, I started exploring angular js and faced a challenge in formatting json data with the help of angular js. Below is a snippet of my json data: [ {"field_add_link": "<a href=\"/drupal3/drupal3/\">Home</a>"}, {"field ...

Maintain Angular Dropdown Menu Open Across Page Refresh

I am currently working on an HTML/Typescript view that is connected to a SQL Database. Whenever there are changes made to the database, the entire webpage reloads. The issue we are facing is that we have dropdown menus on the page that clients want to rema ...

Guide on implementing themes to HTML within the append() function

I am currently working on a project where I need to dynamically add HTML tags using JavaScript. However, I have noticed that the themes or styles are not being applied to the newly added elements within the append method. In my HTML file, I am using jQue ...

Tips for resolving the issue: React is unable to recognize the X prop on a DOM element

I have been experimenting with a library known as react-firebase-js for managing firebase authentication. However, my grasp of react and the concept of provider-consumer is somewhat limited. Initially, I created a large JSX construct at the top level, whi ...

Fill in a data table beginning with the column next to the first

My issue involves a datatable retrieving JSON data from an API. The table is configured so that the first column should only display a checkbox. However, upon data retrieval, the first column gets populated as well. $.getJSON('https://api.myjson.co ...

How to transfer data from an HTML form to PHP using AJAX

I've encountered an issue while working on a simple application that consists of one HTML file communicating with a PHP page using AJAX. Take a look at my index.html below: <!DOCTYPE html> <html><head> <meta charset="utf-8"> & ...

In Javascript, comparing a regular array value with an array value created by the match function

I'm experiencing a problem with comparing values in two different arrays. Here is the code snippet: tagNames = []; tagNames.push('61'); cmt_wrds = '‏‏61'.replace(/[`~!@#$%^&*()_|+\-=?;:&apos ...

Process JSON data from an input using Javascript

I am encountering an obstacle at the final step of my data flow process. Currently, I am in the midst of developing an application that retrieves input from an HTML form field and utilizes Ajax to fetch data relevant to the user's input. Allow me to e ...

Determine the number of items (within an array) that were created within the past few days, weeks, and months leading up to the 'current time'

Having an array filled with objects containing timestamps: Here is a glimpse of the data: const dataList = [ { _id: "602102db3acc4515d4b2f687", createdDt: "2021-02-08T09:22:35.000Z", }, { _id: "6021024da706a260d89 ...

What is the best way to change a canvas into an image while preserving transparency?

Currently, I am attempting to modify a weather radar image with a black background by making the background transparent using canvas. However, when I view the modified image, instead of transparency, the background now appears as a red and black checkerboa ...

Different perspectives displayed simultaneously on a single page, achieved without the need for routes

On my page, users have the ability to sort items using various filters. When the filter is set to Newest, the items are simply listed by name. But when the filter is set to By collection, the items within a specific collection are displayed under that col ...

Is there a way to change the color of a row in react jsx based on a condition from another row?

I am looking to highlight rows in red color based on a specific condition. If the ratio value in a column is greater than or equal to 0.96, I want to apply this styling. However, I'm unsure about where exactly to insert the necessary lines of code to ...

Can React components receive props or data when they are inserted into regular HTML code?

I have a project where I need to make updates to an old-fashioned website. The current layout is table-based and coded by hand. My idea to streamline the process is to use React to minimize repetitive coding tasks. Specifically, I want to loop through an a ...

Detect if the user is using Internet Explorer and redirect them to a different

My web application is having trouble rendering in Internet Explorer. In the meantime, I would like to detect if the user is using IE and redirect them to a different page specifically for IE visitors. What is the best way to accomplish this? Should I use ...

I'm having trouble receiving a response after uploading an image on Cloudinary using React js

Once the image is uploaded using the API, it should return a response. However, I am not receiving any response through the API even after uploading the image. if (pic.type === "image/jpeg" || pic.type === "image/png") { const da ...

Prioritize loading one script over the other in Next.js/React

In the head section of my webpage, I have included two scripts to load: <Head> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script> <script src="/static/js/myscript.min.js" ...

Perform a Fetch API request for every element in a Jinja2 loop

I've hit a roadblock with my personal project involving making Fetch API calls to retrieve the audio source for a list of HTML audio tags. When I trigger the fetch call by clicking on a track, it always calls /play_track/1/ and adds the audio player ...

Issue with Angular: ngModel doesn't update when used within ngInclude

Before anything else, check out the Plunker demo: http://plnkr.co/edit/v1uTz5 I am encountering an issue and this demo showcases it in action. In my code, I am using ng-include to include a partial template. Within this partial template, there is a text ...

What steps should be taken to refresh an Expo app block once new data is retrieved from the database

I'm facing an issue with connecting my expo app to a database. I have utilized react redux to fetch data from the database, and upon successful retrieval of data, I aim to update my furniture block with the obtained information. However, despite recei ...

Getting the length of child elements in Angular using ngFor loop

Can anyone help me figure out how to check the length of a child element in my Angular *ngFor loop? I am fetching data from a real-time firebase database. What am I doing wrong? Here is the code snippet I am using: <div *ngFor="let event of events"> ...