Despite awaiting them, promises are not resolving synchronously

I have a function that retrieves location information and returns a promise. I use mobx to manage the store, updating the this.locationStoreProp and this.hotel.subtext properties.

public fetchPropertyLocation(some_input_params): Promise<any> {

    return this.apiSource
      .fetchPropertyLocationData(input)
      .then(({ data }) => {
        runInAction('update from response', () => {
          if (data) {
            this.locationStoreProp = data.location;
            this.hotel.subtext = data.location.score;
          }
        });
      })
      .catch(error => {
        runInAction('error in location', () => {
          //log event
        });
        return {
          error
        };
      });
  }

The issue arises because the this.hotel property depends on another fetch operation below, which must complete before the above promise can set the subtext in this.hotel. However, the fetchHotelInfo function is used inside another fetch (below it).

public fetchHotelInfo(input_params): Promise<any> {

    return this.hotelInfoSource
      .fetch(input)
      .then(data => {
        runInAction('update hotel from response', () => {
          this.hotel = data;
        });

        return data;
      })
      .catch(error => {
        runInAction('recovering from hotel info call failure', () => {
          //log event
        });

        return {error};
      });
  }
  public fetch(input_params) {
    const { hotelId, searchCriteria } = this;

    const infoPromise = this.fetchHotelInfo(input_params);
    const someOtherPromise = this.someOtherOperation(input_params);

    return Promise.all([infoPromise, someOtherPromise])
      .then(
        () =>
          new Promise(resolve => {
            runInAction('updating', () => {

              this.isLoading = false;

              resolve();
            });
          })
      )
      .catch(
        () =>
          new Promise(resolve => {
            runInAction('updating hotel with failure', () => {
              this.isFailure = true;
              resolve();
            });
          })
      );
  }

Finally, when awaiting them, I want the promise of the fetch function to resolve first with its fetchHotelInfo promise. However, currently, my location function's promise resolves first, causing the property this.hotel to be undefined.

public async fetch(options: FetchOptions): Promise<any> {
 await fetchClassObj.fetch(params);
 await fetchClassObj.fetchPropertyLocation(params);
}

What could be going wrong here? Thanks. Also, please disregard any syntax errors for now.

Answer №1

The reason why this.hotel is coming up as undefined is because within your promise callbacks, you are invoking the function runInAction with an additional non-promise callback. This leads to a situation where when the promise resolves, the execution of the callback in runInAction is still pending. To address this issue, I suggest creating a wrapper function like so:

function runInActionPromise(status) {
  return new Promise((resolve, reject) => {
    runInAction(status, resolve);
  });
}

If runInAction is a custom function, consider modifying it to utilize promises instead of callbacks directly.

In addition, it is advisable to stick to either async/await or Promise.then() throughout your codebase, avoiding mixing both approaches. Opting for async/await can help alleviate the callback hell that seems prevalent in your current implementation. For instance, you could refactor fetchPropertyLocation as follows:

public async fetchPropertyLocation(inputParams): Promise<any> {
  try {
    let { data } = await this.apiSource.fetchPropertyLocationData(input);
    await runInActionPromise('update from response');
    this.locationStoreProp = data.location;
    this.hotel.subtext = data.location.score;
  } catch(error) {
    await runInActionPromise('error in location');
    //log event
    return { error };
  }
}

Similarly, adjust fetchHotelInfo like this:

public async fetchHotelInfo(inputParams): Promise<any> {
  try {
    let data = await this.hotelInfoSource.fetch(input);
    await runInAction('update hotel from response');
    this.hotel = data;
    return data;
  } catch(error) {
    await runInAction('recovering from hotel info call failure');
    return { error };
  }
}

This approach enhances readability and facilitates "synchronous" error handling using try/catch.

Regardless of your chosen path, ensure that you accommodate the callback mechanism in runInAction by encapsulating it in promises wherever it is utilized, which should ultimately result in a functional solution.

Answer №2

Consider implementing a solution like the following:

Untested

fetchHotelInfo().then(hotel => {
  this.hotel = hotel;
  return fetchPropertyLocation();
}).then(propertyLocation => {
  this.hotel.prop = propertyLocation.prop;
  this.hotel.bar = propertyLocation.bar;
});

By utilizing this approach, there is no need to intermix async/await and Promises, resulting in cleaner code structure. Given that Promises are intended to supersede callbacks, it is recommended to convert the function runInAction into one as well.

Therefore:

function runInAction(): Promise<void> {
  return new Promise(...);
}

.catch(() => runInAction('updating hotel with failure'));

This adjustment will maintain the continuity of the chain and enhance overall code consistency.

Crucial Note: Introducing a callback within such a chain may disrupt the sequential execution order. Hence, refrain from using callbacks (unless they incorporate resolve()) within promises.

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

Having trouble with submitting data in an ExpressJS POST request while using mongoose?

As I embark on building my first express.js application, I encounter my initial obstacle. The setup is rather simple. Routes in app.js: app.get('/', routes.index); app.get('/users', user.list); app.get('/products', product. ...

Do we really need to use the eval function in this situation?

Just wondering, is it reasonable to exclude the eval() function from this code? Specifically how <script> ... ... function addGeoJson (geoJsonPath, iconPath = "leaflet-2/images/marker-icon.png", iconSize = [30,50], popUpContent, ...

how to set a variable's value outside of a Promise using internal code

export class YoutubeService { getTrendingVideos(country) { let result = []; return axios.get('/').then(function(res){ result = res.data.items; for (var i = 0; i < result.length; i++) { result[i] = { id: ...

Splitting JavaScript Arrays: Exploring Efficient Division

I'm attempting to develop a recursive function that divides an array in half until it only consists of lengths of 3 and 2. After dividing, I want to neatly organize all the new arrays into another array. My idea is to find a way to determine the numb ...

When sending a request from Vue.js using Axios to PHP, the issue arises that the .value property is

There is a chat box with bb smileys underneath. Clicking on the smileys adds them to the text in the input box. However, when using axios to post, the array is empty. Manually entering the smiley bbcode works fine. <input id="txtName" @keyup.enter="ad ...

What is the most effective method for displaying error messages in Extjs?

I am currently using the store.synch() method to post data, with validation being done on the server side. I am currently displaying error messages in a message box, but I want to explore alternative ways to show errors without having to use markInvalid( ...

Retrieving all the information stored in the tables

I'm struggling with retrieving the content of my table cells. Some cells contain a hyphen (-) and if they do, I want to center the text. I'm facing difficulties with my jQuery code, particularly the if statement which always evaluates to false. ...

sending an array from one CodeIgniter view to another view via Ajax

In the following code segments of my application, myArray is an array where each element contains a few objects that I need to use in a second view. When I use alert(myJSON);, I am able to see the array in the alert window. However, when the view loads, i ...

Challenges with Cross-Origin Resource Sharing (CORS) when accessing Uber API OAuth

I am encountering an issue while attempting to make client-side JS calls to Uber API endpoints that require the OAuth Bearer token, such as /v1/me. The problem arises due to the absence of the Access-Control-Allow-Origin header in the response. I have suc ...

Unable to locate the value property of a null object

I'm currently working on creating a Shopping Cart using HTML, CSS, JavaScript, and JQuery. The idea is that when you click "Add to Cart" for the orange item, most of the HTML elements will disappear, leaving only the table displaying the Shopping Cart ...

How can I get electron to interact with sqlite3 databases?

I've exhausted all my options and still can't get it to function. This error message keeps popping up: https://i.stack.imgur.com/D5Oyn.png { "name": "test", "version": "1.0.0", "description": "test", "main": "main.js", "scripts": { ...

jQuery document.ready not triggering on second screen on Android device

Why is jQuery docment.ready not firing on the second screen, but working fine on the first/initial screen? I also have jQuery Mobile included in the html. Could jQuery Mobile be causing document.ready to not work? I've heard that we should use jQuery ...

Loading Disqus comments dynamically upon clicking a button within a Next.js application

After noticing a significant decrease in page performance scores due to Disqus comments embedded on Vercel Analytics, I am considering implementing a "Load comments" button instead of loading the actual comments onClick. I have been using the disqus-react ...

Is there a way to modify the dimensions of this container? (CSS)

After performing a search and clicking on a result, a white bubble pops up on the map. I am looking to use CSS to make this bubble smaller as it is currently too large. Can anyone provide guidance on how to achieve this? ...

Exploring Error Handling in AngularJS and How to Use $exceptionHandler

When it comes to the documentation of Angular 1 for $exceptionHandler, it states: Any uncaught exception in angular expressions is passed to this service. https://code.angularjs.org/1.3.20/docs/api/ng/service/$exceptionHandler However, I have noticed ...

Update the CSS for InputLabel

I have a drop-down list that I want to customize. The issue is illustrated below: I'm looking to center the text "choose format" within the field and adjust the font size. return ( <FormControl sx={{ m: 1, minWidth: 150 }} size="sm ...

I have the latitude and longitude for both the shop and user, and I am looking to display a list of shops in order based

Currently, I have the latitude and longitude for both my shop and the user. My objective is to display a list of shops that fall within the geographic area between the user's location and the shop's coordinates using Sequelize ORM. Can you provid ...

Ensuring that the desired DOM elements have loaded before attempting to manipulate them in Vue.js

I've been struggling with this issue for the past day and I'm completely stuck. In my vue file, there's a method that operates like this: methods: { showSlides(n) { let slides = document.getElementsByClassName("mySlides"); ...

What is the best method to eliminate elements from a queue using JavaScript?

I recently attempted to incorporate a queue feature into my JavaScript code. I found some helpful information on this website. While I successfully managed to add items to the queue, I faced difficulties when attempting to remove items from it. var queue ...

Execute a simulated click function in JavaScript without causing the viewport to move

I have successfully implemented a sticky add to cart feature on my Shopify store. However, there seems to be an issue where clicking on the variations in the sticky area also triggers the same variations on the product page, making it difficult for users t ...