The chaining of Angular async promises is not working properly, causing the 1st call to not return in time for the 2nd call to

Before making a second service call, I need to retrieve a zipcode. My initial plan was to obtain the zipcode before calling the 2nd service by wrapping it in a promise, so that I could access the promise later. However, chaining promises in this way is proving to be difficult.

Angular factory is invoked and within the factory method:

var userGeoPromise = userService.getGeoposition().then(function (geoposition) {
    vm.geoposition = geoposition;
            return addressService.reverseGeocode(geoposition.coords);
    }).then(function (data) {
            vm.currentLocation = googleService.googleAddressComponentsToAddress(data.results[0]);
            zipCodeCurrent = vm.currentLocation.zip;
    });

A couple of points to note from the code above:

  1. I stored the promise in var userGeoPromise
  2. zipCodeCurrent is populated with the obtained zipcode

Testing the Promise works as expected:

userGeoPromise.then( function() {
      console.log('should always display', zipCodeCurrent);
});

2nd service call:

userGeoPromise.then( function() {
     var serviceBase = "http://localhost:2295/api/getservicezip/"+ zipCodeCurrent;
     var serviceZipPromise = $http.get(serviceBase);
     return serviceZipPromise.then(function (results) {
          console.log('serviceZipPromise', results);
          return results.data;
     });
});

However, upon including the serviceZipPromise.then within the other promise, the website modal simply keeps spinning without progress.

Answer №1

When working within a then callback, it is important to remember to return the result value instead of assigning it directly to a variable like zipCodeCurrent. While the first then callback in your code follows this principle correctly, the second one should also adhere to it:

var userGeoPromise = userService.getGeoposition().then(function (geoposition) {
    vm.geoposition = geoposition;
    return addressService.reverseGeocode(geoposition.coords);
}).then(function (data) {
    vm.currentLocation = googleService.googleAddressComponentsToAddress(data.results[0]);
    return vm.currentLocation.zip; // *** make sure to return it
});

It's worth noting that I have left the assignments to the properties of vm, but ideally you should avoid mutating variables that seem to exist outside the scope of these callback functions.

The Promise test can be structured as follows:

userGeoPromise.then( function(zipCodeCurrent) { // *** ensure to include the argument
    console.log('should always display', zipCodeCurrent);
});

The second service features a nested then call, which is generally considered best practice to avoid. Instead of nesting promises, it is recommended to return the promise and apply the then on the main chain:

userGeoPromise.then( function(zipCodeCurrent) { // *** include the argument for consistency
    var serviceBase = "http://localhost:2295/api/getservicezip/"+ zipCodeCurrent;
    return $http.get(serviceBase); // *** return the promise instead
}).then( function (results) { // *** move the then-callback to the outer chain
    console.log('serviceZipPromise', results);
    return results.data;
}).catch( function (error) { // *** handle errors at the end of the chain
    console.log('an error occurred:', error);
});

Notice how the nesting level never goes beyond 1 for better code readability and organization.

Answer №2

Modify the sequence and include 2 separate error handlers (which is considered a good practice, by the way)

var serviceZipPromise = $http.get(serviceBase); // invoke
        return serviceZipPromise.then(function(results) {
            console.log('should always show', zipCodeCurrent);
            userGeoPromise.then(function() {
                //console.log('serviceZipPromise', results);
                console.log('inside servicezip ', zipCodeCurrent);

            }, function(err) { // handle error from userGeoPromise
                console.log(err);
            });

            return results.data; // THIS will return the data
        }, function(err) { // handle outer error, this was swapped 
            console.log(err);

        });

This section should not produce an error. However, to ensure that your actual serviceBase utilizes the zip code correctly, you may need to delay its execution slightly.

UPDATE answer provided for you

// initialize part of the api call
var xserviceBase = "http://localhost:2295"; // seems like your base

return userGeoPromise.then(function(){
       return $http.get(xserviceBase + '/api/getserviceablezip/' + zipCodeCurrent).then(function(results){
             return results.data;
       })
  });

I realize that having 3 return statements might seem messy, but it should function as intended

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

Comparing ngrx and redux for managing state in stateless components

Exploring ngrx/angular 8 for the first time, I'm curious to know if the angular approach of using an observable to bind a state value to the this context still allows a component to remain presentational and stateless. In the realm of angular/ngrx, c ...

What is the best way to structure a web server along with a socket.io server?

I am curious about the capabilities of node.js Is it feasible to utilize multiple server.js files? For example, could there be a primary "server.js" that directs users to another directory where a separate server.js manages websocket connections using so ...

Incrementing pages, starting with page1.html and continuing to page2.html, page3.html, and beyond, for use with the window

How should I handle this situation? My goal is to automatically navigate to the next page once all necessary functions have been completed. For instance, after all functions on page1.html are finished, it will trigger a function called next_page(). The n ...

Unable to execute the new firebase on node server

I'm currently watching this google talk. However, I am facing an issue trying to create a firebase object. Following my package.json file, I initiated with: npm install firebase --save Then proceeded with the following steps: let Firebase = requir ...

I'm curious about the process by which custom hooks retrieve data and the detailed pathway that custom hooks follow

//using Input HOOK I am curious to understand how this custom hook operates. import { useState } from "react"; export default initialValue => { const [value, setValue] = useState(initialValue); return { value, onChange: event =&g ...

Transferring data from an Express server to a React frontend perspective

I am relatively new to working with the MERN stack, so please forgive me if this question sounds novice. I am currently developing an application that is designed to visit a specific URL, extract some text data from it, and then display this information on ...

Is it possible to authorize all requests for a file's content with cross-domain AJAX calls?

I am looking to share a banner from my website on other sites while keeping the banner hosted on my server. I would like other sites to include the banner using JavaScript, similar to Facebook plugins and Google ads. The banner is located on site A. On si ...

Tips for excluding the mention of the final index within a for loop when passing the index as a parameter for a function

I'm struggling with the code I've written: function(customSlider, images){ // create thumbs container var thumbContainer = $("<div></div>").addClass(defaultOptions.thumbsContainerClass); // add thumbs ...

What is the process for obtaining an AccessToken from LinkedIn's API for Access Token retrieval?

We have successfully implemented the LinkedIn login API to generate authorization code and obtain access tokens through the browser. https://i.sstatic.net/0dfxd.png Click here for image description However, we are looking to transition this functionality ...

Nuxt Vuex global state update does not cause v-for loop components to re-render

I am struggling to effectively use Vuex global state with re-rendering child components in Vue.js. The global state is being mutated, but the data does not update in the v-for loop. Initially, all data is rendered correctly. However, when new data is intr ...

Changing the value of a JavaScript variable within the .Net Codebehind

I've run into an issue where I need to update a JavaScript variable after post-back. My initial approach was to use the ClientScript.RegisterStartupScript function, which worked fine during the first page load but failed on subsequent postbacks. I inc ...

Adjusting or cropping strokes in a JavaScript canvas

I am working with a transparent canvas size 200x200. The object is being drawn line by line on this canvas using the solid stroke method (lineTo()). I'm in need of making this object full-width either before or after ctx.stroke();. https://i.sstatic. ...

Issues with Angular route links not functioning correctly when using an Array of objects

After hard coding some routerLinks into my application and witnessing smooth functionality, I decided to explore a different approach: View: <ul class="list navbar-nav"></ul> Ts.file public links = [ { name: "Home&quo ...

The impact of scope on XMLHttpRequest

Bounty Note: I am curious about why I do not need to be concerned about the removal of goGet before the asynchronous request is complete. I have a PHP-generated form with multiple HTML rows called "Entries", each containing options for "Delete" and "Edit" ...

Does JavaScript run before Liquid processing in Shopify?

I am encountering an issue when attempting to utilize JavaScript to call Shopify asset URLs and dynamically combine a string into the liquid code. The process throws an error, leading me to believe that Liquid is being processed before the JS. function lo ...

Strategies for managing a sizable td Input element in a Table that undergoes re-rendering whenever there is a change in state in React

I'm not entirely certain if this is the best practice for rendering a table element in React, but it's what I've been doing consistently. The for loop below will be executed each time a re-render occurs, or when there is a change in input. W ...

Filtering rows in JQgrid is made easy after the addition of a new record

Here's the situation I'm facing: Every second, my script adds a new record using the "setInterval" function: $("#grid").jqGrid('addRowData', id, data, 'first').trigger("reloadGrid"); However, when users apply filters while t ...

Activate event starting from parent and ascending through child nodes

When I have a table inside a <div class="timely"></div>, and within that table is a <th class="prev"><i>previous</i></th>, Chrome developer tools show an event listener on the <th>. However, Firefox developer tools ...

The React-loadable alert indicated a discrepancy in the text content

Utilizing react-loadable for dynamic JS module loading is part of my process. With server-side rendering already set up and functioning correctly for react-loadable, I am encountering an issue on the client side. Upon page load, a warning message appears i ...

Conceal an element upon clicking anywhere but within it

I am attempting to create a function where an element is hidden whenever the user clicks outside of it. When the CART link in the top left corner is clicked, a dropdown appears. However, I am encountering two issues. 1) The dropdown is being shown and th ...