1

I'm new to JavaScript and React and am trying to move away from tutorials so have started making a simple app for my own learning benefit but have run into a roadblock with functions running asynchronously.

In onSearchSubmit, there is a setState which has the following in its callback:

this.findSalaryRangeMin(data.advertiser.id, data.teaser);
this.findSalaryRangeMax(data.advertiser.id, data.teaser);

How can I get these two functions above to run synchronously? findSalaryRangeMax uses this.state.salaryLower which is set in findSalaryRangeMin, but the console.log below reveals that findSalaryRangeMax is firing before findSalaryRangeMin has completed.

  findSalaryRangeMax = (advertiserId, teaser) => {
    console.log(`this.state.salaryLower: `, this.state.salaryLower);
    // ... More code
  };

I've read some resources which mention using promises, but I wasn't able to figure out how to apply it... I also am wondering whether it can be achieved with async/await.

Full(ish) Code: (I've removed some code for simplicity)

import React from "react";
import JobSearch from "../api/jobSearch"; // axios
import SearchBar from "./SearchBar";

class App extends React.Component {
  state = {
    jobTitle: "",
    advertiser: "",
    salaryLower: "",
    salaryLowerTop: "",
    salaryUpper: "",
    salaryUpperTop: ""
  };

  findSalaryRangeMin = (advertiserId, teaser) => {
    this.setState({ salaryLower: 0, salaryLowerTop: 200000 }, async () => {
      let salaryLowerPrev;
      for (var i = 0; i < 20; i++) {
        const response = await JobSearch.get(
          `http://localhost:3001/salary-range/${advertiserId}/${this.state.salaryLower}/${this.state.salaryLowerTop}/${teaser}`
        );
        console.log(response);
        if (response.data.totalCount === 1) {
          salaryLowerPrev = this.state.salaryLowerTop;
          this.setState({
            salaryLowerTop: Math.round(
              (this.state.salaryLowerTop - this.state.salaryLower) / 2 +
                this.state.salaryLower
            )
          });
        } else {
          this.setState(
            {
              salaryLowerTop: salaryLowerPrev
            },
            () => {
              this.setState({
                salaryLower: Math.round(
                  (this.state.salaryLowerTop - this.state.salaryLower) / 2 +
                    this.state.salaryLower
                )
              });
            }
          );
        }
      }
    });
  };

  findSalaryRangeMax = (advertiserId, teaser) => {
    console.log(`this.state.salaryLower: `, this.state.salaryLower);
    // ... More code
  };

  onSearchSubmit = async term => {
    const response = await JobSearch.get(
      `http://localhost:3001/job-info/${term}`
    );
    if (response.data.totalCount === 1) {
      const data = response.data.data[0];
      this.setState(
        {
          jobTitle: data.title,
          advertiser: data.advertiser.description
        },
        () => {
          this.findSalaryRangeMin(data.advertiser.id, data.teaser);
          this.findSalaryRangeMax(data.advertiser.id, data.teaser);
        }
      );
    } else {
      console.log("totalCount not equal to 1: ", response.data.totalCount);
    }
  };

  render() {
    return (
      <div>
        <SearchBar onSearchSubmit={this.onSearchSubmit} />
        <hr />
        <div>
          Job Title: {this.state.jobTitle}
          Advertiser: {this.state.advertiser}
          Salary Lower Range: {this.state.salaryLower}
          Salary Upper Range: {this.state.salaryUpper}
        </div>
      </div>
    );
  }
}

export default App;

To give some context, the app I'm trying to make, queries an API for a jobs listing site. The API response doesn't reveal a salary range for an individual job, but the salary can fairly accurately be determined by querying salary ranges.

4
  • @Dupocas Thanks for the quick reply + link. I understand it's the same concept, but I was hoping for a more JS specific context Commented Nov 11, 2019 at 18:34
  • What do you mean by "js specific context" ? Commented Nov 11, 2019 at 18:35
  • Something in JavaScript. The accepted answer for that linked question is in a C# context. Trying to learn a new concept is hard enough, let alone having it explained in a programming language I don't know anything about. Commented Nov 11, 2019 at 18:38
  • Wow. That was my bad actually. I messed up the links (sorry about that): stackoverflow.com/questions/44512388/… Commented Nov 11, 2019 at 18:48

2 Answers 2

1

You are correct in your understanding that async or promises are needed if you want the functions to run synchronously and the existing code will run to the following line findSalaryRangeMax before returning with the data needed.

async/await and promises will definitely help, but often it's worth considering a few code changes too. As an example, you could combine the two functions into a single function like

findSalaryRanges(data.advertiser.id, data.teaser)

and fetch the data, process and set state once.

some pseudo code:

findSalaryRanges = async () => {
    // get all the data needed first
    const maxSalaryData = await JobSearch.get(`http://localhost:3001/salary-range/${advertiserId}/${this.state.salaryLower}/${this.state.salaryLowerTop}/${teaser}`);
    const minSalaryData = await JobSearch.get(...);

    // process data as needed
    ...

    // set state once
    this.setState({
        salaryTop: salaryTop,
        salaryLower: salaryLower
    });
};
Sign up to request clarification or add additional context in comments.

2 Comments

I ended up giving the code a complete overhaul. I couldn't do it quite the way that you proposed as my application requires to compare API results and submit another query based on result, but it pointed me in the right direction.
No prob, sometimes you just need a sanity check to help you get a few ideas
0

setState is async, so if you are dependent on the value of the state before running the next function you could do something like:

this.setState({
  salaryLowerTop: Math.round(
    (this.state.salaryLowerTop - this.state.salaryLower) / 2 +
      this.state.salaryLower
    )
}, () => this.findSalaryRangeMax(data.advertiser.id, data.teaser))

Can I execute a function after setState is finished updating?

2 Comments

I've tried using .then(), but I get the following error when I use it: Unhandled Rejection (TypeError): Cannot read property 'then' of undefined
I've edited my answer, it's been a while since I've worked w/ class components :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.