Executing a sequence of two asynchronous calls in a serial manner using the promise API

I recently posted a question on Stack Overflow that was somewhat similar to this, but the current issue requires chaining the requests serially. I have two asynchronous requests where the second request depends on the result of the first request in order to send its query.

var Db.get = function(key){
    var deferred = $q.defer();

     //send async req
    var req = ....
    req.success = function(d){
        deferred.resolve(d)
    };
    req.failure = function(d){
        deferred.reject(d)
    }

    return deferred.promise;
}

var someFn = function(id){
    Db.get(id, "abc")
        .then(function (d) {
            console.log("At 1")
            Db.get(d.id, "def")
                .then(function (d) {
                    console.log("At 2")
                    return d
                }, function (e) {
                    //error
                });
        }, function (e) {
            //error
        });

    console.log("At 3")
};

In my understanding, I expected console.log("At 3") not to be printed in the success scenario as I return after console.log("At 2"). However, when I run the code, the following order is seen in the console:

console.log("At 1")
console.log("At 3")
console.log("At 2")

I thought that the then method would block until it receives a response from the promise returned by get(), ensuring that everything in someFn executes serially. Is this assumption incorrect? What is the best way to chain two asynchronous operations that use promises to run serially?

Thank you.

EDIT:

I attempted what Ketan suggested regarding Chaining Ajax calls in AngularJs.

var someFn = function(id){
            Db.get(id, "abc")
                .then(function (d) {
                    console.log("At 1")
                    return Db.get(d.id, "def")
                }).then(function (d) {
                    console.log("At 2")
                    return d
                }, function (e) {
                    //error
                    return null;
                }).then(function (d) {
                    return d;
        });

        console.log("At 3")
    };

Even after making a call like

var res = someFn(1)
console.log(res) /// undefined

The Chrome terminal displays At 2 after undefined. I am unsure why the result returned by someFn is not assigned to res.

Answer №1

One issue you may be facing is that the .then function does not actually block. It assists in converting synchronous code to asynchronous code, but it does not perform this conversion automatically. Let's consider the synchronous code you are attempting to rewrite. Imagine if Db.get function was synchronous and returned a value instead of a promise:

var someFn = function (id){
    try {
        var d = Db.get(id, "abc");
        console.log("At 1");
        var d = Db.get(d.id, "def");
        console.log("At 2")
        return d;
    } catch (ex) {
        console.log("At 3")
    }
};

In this scenario, calling someFn() would give you a value synchronously, not a promise. The entire function runs synchronously.

If we jump ahead a few years and imagine using ES6, we could rewrite the function as follows:

var someFn = $q.async(function* (id){
    try {
        var d = yield Db.get(id, "abc");
        console.log("At 1");
        var d = yield Db.get(d.id, "def");
        console.log("At 2")
        return d;
    } catch (ex) {
        console.log("At 3")
    }
});

Although it looks similar, now Db.get returns a promise, making someFn() always return a promise. The yield keyword pauses the function until the promise resolves, simulating synchronous behavior in an asynchronous environment.

Returning to the present, let's see how to write this. The second argument of a .then call serves as an error handler. The equivalent of the ES6 example would be:

var someFn = function (id){
    return Db.get(id, "abc")
        .then(function (d) {
            console.log("At 1");
            return Db.get(d.id, "def");
        })
        .then(function (d) {
            console.log("At 2");
            return d;
        })
        .then(null, function (ex) {
            console.log("At 3")
        });
});

Each return statement only exits the current function scope; there is no way to make it jump out of someFn.

Here's an interesting experiment to try:

Db.get('id', 'abc')
  .then(function () {
    console.log('B');
  });
console.log('A');

The output will always be:

A
B

since .then does not block.

Answer №2

I had the idea that it would wait until it received a response from the promise

Incorrect. In JavaScript, promises do not block future operations; they are simply a way to chain callbacks. The promise is returned before the callback is executed - so At 3 is logged after .then returns, but before the callback is actually executed. Additionally, if you use return within the callback, it does not affect the outer function someFn.

A better approach would be something like this:

var someFn = function(id){
    return Db.get(id, "abc")
      .then(function (d) {
        console.log("At 1")
        return Db.get(d.id, "def");
      })
      .then(function (d) {
        console.log("At 2")
        return d
      }, function (e) {
        //handle error
      });
}
someFn().then(function(d) {
    console.log("At 3")
});

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

Issues encountered during npm installation of generator-angular

The following commands were executed: AT07684S@ZE0PW0NM /d/AAA_Dev2015 $ npm install -g generator-karma --save npm WARN deprecated <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="0e62616a6f7d664e3c203a203c">[email prote ...

Saving the image logo into local storage during page loading process in AngularJS

I am looking for a way to save an image logo to local storage when a page is loaded in my AngularJS application. angular.module('app', [ 'ngStorage' ]). controller('Ctrl', function( $scope, $localStorage ){ ...

Having issues with AngularJS routing functionality not functioning as expected

Currently, I am delving into AngularJS as part of a project I'm working on. Initially, AngularJS seemed like a promising framework, offering great support and user-friendly features. However, I have hit a roadblock when trying to implement routing... ...

Encountering issues with parsing normals in THREE.js json mesh format

UPDATE: The demo is finally live! Check it out here: . Use the dropdown menu to switch between torus models and see the issue in action. Please note that WebGL MRT extensions are required for this demo. I have been working on developing my own WebGL defer ...

The Javascript style.opacity function eliminates the leading zero from the input string

In my application, users have the ability to change the color of the background but not the pattern. They can adjust the background behind the pattern and the opacity of the pattern. Users are prompted to input a percentage, which is actually a number betw ...

The function cannot be applied to the size of the map within the action payload

Is there a way to replace the for loop with the map method? The data structure for book.pages is in the format [{},{},{}] I tried using the size method and included this line console.log("book.pages.map.size();--->", book.pages.map.si ...

I encountered a "ReferenceError: db is not defined" while trying to call a function in Express.js with MongoDB intergr

The structure of the code is as follows: var express = require('express'); var router = express.Router(); var mongo = require('mongodb').MongoClient; function getData(){ db.collection("collection_name").find({}).toArray(function (er ...

Adjust Element Width Based on Scroll Position

I'm attempting to achieve a similar effect as seen here: (if it doesn't work in Chrome, try using IE). This is the progress I've made so far: http://jsfiddle.net/yuvalsab/op9sg2L2/ HTML <div class="transition_wrapper"> <div ...

How to use JavaScript to retrieve extensive data streams (exceeding 1GB) from the internet

I'm interested in finding out if there is a way to stream data directly from JavaScript to the browser's download manager. With WebRTC, I am able to stream large amounts of data (files over 1GB) from one browser to another. On the receiving end, ...

I'm encountering a problem with handling errors in Express.js: A critical error has occurred while attempting to execute the _runMicro

Currently, I am utilizing the Blizzard API Battle.Net to fetch information regarding a character's name and the realm they inhabit. In certain cases, a user may request a character that does not exist, prompting Blizzard to return a 404 error response ...

What steps are involved in integrating OpenCV into a JavaScript project?

After recently installing OpenCV via npm using this guide: https://www.npmjs.com/package/opencv I'm facing a simple question. How can I actually utilize the OpenCV library in my project? The site provides a face detection example code snippet: cv.r ...

Custom Sign-in features in NextJS now direct users to the default form for entering login

I have been working on a web app that requires authentication using NextJS default auth middleware. However, whenever I try to log in, the app redirects me to the default NextJS form component at the URL /api/auth/signin?error=CredentialsSignin. Even thou ...

Extract information from a JSON file to populate a jQuery Datatable

I am attempting to include data from a JSON file that was created using a Django script. Here is the structure of the JSON file: [ { "6": "yo1", "1": "2019-04-04", "4": "yo1", "3": "yo1", "2": "yo1", "5": "yo1" }, { "6": "yo2" ...

Updating Select Options with Multiple Values using Javascript

My goal is to update the selected value of multiple select elements simultaneously using JavaScript. However, I am facing an issue where my script only updates one select element instead of all of them on the page. Unfortunately, I cannot modify the id att ...

Obtain the URL linked to the button that was just clicked

As a newcomer to jQuery, I have a question: For instance, on a webpage with a voting feature, when a button is clicked, a counter is increased by +1. Now, how can the URL of this button be displayed on a website? This way, if the URL is shared with others ...

The jQuery UI Dialog is experiencing an issue with a button that is triggering a HierarchyRequest

I am facing an issue with a piece of javascript that works perfectly on other pages but is now throwing a HierarchyRequestError on a new page. This leads me to believe that there may be an HTML problem on this particular page. Here is a simplified version ...

Using only Node.js, demonstrate the image

I want to show an image using only Node.js without involving HTML or ID's. I have been looking for solutions but most examples I find use HTML, which I prefer not to use. Unfortunately, I don't have any code to share, but I'm wondering if th ...

Transmit the Selected Options from the Checkbox Categories

Here's an intriguing situation for you. I've got a webpage that dynamically generates groups of checkboxes, and their names are unknown until they're created. These groups could be named anything from "type" to "profile", and there's a ...

Continue running the remaining part of the function once the asynchronous function has completed its execution

To obtain the last 4 digits of a payment using Stripe, I need to execute an async function that contains another async function. Once these functions are resolved, I aim to update the database with the last four digits. It is crucial to ensure that the dat ...

Troubleshooting unresponsive buttons within a $timout/ setTimeout function in Angular and Ionic

I am trying to implement a popup with a slight delay. However, I am encountering issues where the buttons are not functioning properly without the delay, and they do not appear when I add the delay. I have come across information online mentioning that $ti ...