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

Transforming button properties into a JSON format

I am currently in the process of developing a web application using node.js and mongodb. Within my app, there is a table that dynamically populates data from the database using a loop. I encountered an issue with a delete function that I implemented base ...

What is the best way to retrieve a specific key from a JavaScript Map containing an array?

I am currently iterating through a two-dimensional array to populate a map. The map is using the [i,j] indices as keys and the corresponding arr[i][j] values as values: const arrMap = new Map() for(let i = 0; i < arr.length; i++){ for(let j = 0 ...

What could be causing the Ioncol not triggering the Onclick event?

I am facing an issue where my onclick event is not working on an ion-col. The error message says that the method I call "is not defined at html element.onclick". Here is a snippet of my code: <ion-row style="width:100%; height:6%; border: 1px solid # ...

Implementing a Response to an AJAX POST Request with Servlets

I have a unique situation where I need to validate a username input in a text box. The process involves sending the entered username to a server for checking if it has already been taken by another user. If the username is available, I want to change the b ...

`How to utilize the spread operator in Angular 4 to push an object to a specific length`

One issue I'm facing is trying to push an object onto a specific index position in an array, but it's getting pushed to the end of the array instead. this.tradingPartner = new TradingPartnerModel(); this.tradingPartners = [...this.tradingPartner ...

Unlocking full content access on newapi.org is just a few simple steps away

Currently, I am in the process of building a website using the newsorg API. After sending a request to the API, below is the sample output that I received: "articles": [ -{ -"source": { "id": null, "name": "Hind ...

Creating JSON from identical user interface components

I have created a form similar to this one: https://jsfiddle.net/6vocc2yn/ that generates a JSON output like below: { "List": [ { "Id": 10, "Name": "SDB_SOLOCHALLENGE_CHALLENGE_DESC_10", "email": "<a href="/cdn-cgi/l/email-pr ...

CSS and Javascript functioning correctly within internal server, but encountering issues when accessed externally

I am in the process of creating a website for a friend. The goal is to have a flashy animated style website that functions well on IOS and allows him to make changes easily. To achieve this, I am utilizing JQuery, my own javascript, and multiple css files. ...

Activate the Chrome Extension that allows you to open a link in a new tab with just a middle click or regular click, without closing the popup

When I try to click a link in my extension popup and open it in a new tab using "middle click -> open link in a new tab", the popup closes. Is there a way to keep the popup open so I can click on multiple links from my extension without interruption? A ...

Customizing jQuery dialog: What is the best way to change the appearance of the close button?

I am struggling to style the close tag in my jQuery dialog box. I have looked through the documentation and tried various CSS alterations, but nothing seems to be working. Any suggestions or insights would be greatly appreciated. My code is provided below ...

Create a JavaScript script within a CSS file

I'm attempting to create this using only CSS: Codepen and I would like to achieve the same effect but solely with CSS. This is what my CSS looks like: .text{ --a: calc(var(--a);*0.82+(1.5-var(--b);)/10); --b: calc(var(--b);+var(--a);); transfor ...

modifying the click state using a variable in jquery

Something feels off about my approach to this task. I currently have a series of hyperlinks, and when they are clicked, they go through a short sequence before changing states. When clicked again, they revert to their original state. var favourites = fun ...

Error: Cannot access collection property of dbObject

I've been working on fetching data from a database, but I've hit a roadblock. I keep encountering an error and can't seem to figure out what's causing it. I've searched for solutions but haven't found one that works yet. I&apo ...

Is there a way to delay loading 'div' until a few seconds after the 'body' has been fully loaded?

Is there a way to delay the appearance of the "text-container" div after the image is displayed? I have attempted to achieve this using JavaScript with the following code: <script> window.onload = function(){ var timer = setTimeout("showText()",700 ...

Pressing the Enter key does not initiate a redirect on the page

Hey there! I've set up a text field where users need to input a password in order to download a PDF file. If the password is correct, they are directed to the URL of the PDF file. However, if the password is wrong, they are redirected to a page called ...

Switching the positions of the date and month in VueJS Datepicker

Recently, I have been utilizing the datepicker component from vuejs-datepicker. However, I encountered an issue where upon form submission, the date and month switch places. For instance, 10/08/2018 (dd/MM/yyyy) eventually displays as 08/10/2018, leading ...

Exploring the nuances of receiving responses with NextJs, Nodemailer, and Fetch

Currently in the process of constructing a basic contact form with Next.js, nodemailer, and fetch. Despite successfully sending emails from the frontend form to the designated email account, the network shows the contact submission as pending. After approx ...

What is the syntax for creating a zip function in TypeScript?

If I am looking to create a zip function: function zip(arrays){ // assume more than 1 array is given and all arrays // share the same length const len = arrays[0].length; const toReturn = new Array(len); for (let i = 0; i < len; i+ ...

Refresh the datatable using updated aaData

How do I automatically update the Datatable with new Json data? POST request is used to receive data, which is then sent to the LoadTable function in order to populate the datatable. function initializeTable(){ $("#submitbutton").on( 'click', ...

Using either Canvas.toBlob or Canvas.toDataURL results in me obtaining an image with a transparent

Hey there! I'm currently working on a project that requires the user to crop and upload images. For cropping, I am utilizing react-cropper. My challenge lies in dealing with Chrome's limitation on dataURL to 65529 pixels as mentioned in this M ...