Home > Software engineering >  How to resolve multiple functions that invoke axios get requests within a promise chain?
How to resolve multiple functions that invoke axios get requests within a promise chain?

Time:04-28

Basically I am trying to write a function that will return a list of team scores with the team names next to their score. However because the function which retrieves the team name returns a pending promise I can never see the names in the console.

How do I resolve these promises so that in the next .then block of the promise chain I can see the team names either side of the scores as opposed to [object Promise].

[In this picture you can see the full function with the two fetchTeamNames functions stored in the variables][1]

The fetchFullTimeHeadToHeadScore function is the parent function:

function fetchFullTimeHeadToHeadScore(homeTeamId, awayTeamId) {
  return axios
    .get(
      `https://soccer.sportmonks.com/api/v2.0/head2head/${homeTeamId}/${awayTeamId}?api_token=${apiKey}`
    )
    .then((res) => {
      const homeSide = fetchTeamName(homeTeamId).then((res) => {
        return res;
      });
      const awaySide = fetchTeamName(awayTeamId).then((res) => {
        return res;
      });

      return [homeSide, res.data.data, awaySide];
    })
    .then((data) => {
      return data[1].map((obj) => {
        return data[0]   " "   obj.scores.ft_score   " "   data[2];
      });
    })
    .then((scores) => {
      scores.length = 10;
      return scores;
    })
    .catch((err) => {
      console.log(err);
    });
}

...which calls the fetchTeamName function:

function fetchTeamName(teamId) {
  return axios
    .get(
      `https://soccer.sportmonks.com/api/v2.0/teams/${teamId}?api_token=${apiKey}`
    )
    .then((res) => {
      return res.data.data.name;
    });
}

...which in the console I would expect to enable me to see the names of the teams but currently is only outputting [object Promise]:

- Expected  - 10
  Received    10

Array [
    -   "1-2",
    -   "3-0",
    -   "1-0",
    -   "4-1",
    -   "1-1",
    -   "1-0",
    -   "0-2",
    -   "1-2",
    -   "0-2",
    -   "2-0",
        "[object Promise] 1-2 [object Promise]",
        "[object Promise] 3-0 [object Promise]",
        "[object Promise] 1-0 [object Promise]",
        "[object Promise] 4-1 [object Promise]",
        "[object Promise] 1-1 [object Promise]",
        "[object Promise] 1-0 [object Promise]",
        "[object Promise] 0-2 [object Promise]",
        "[object Promise] 1-2 [object Promise]",
        "[object Promise] 0-2 [object Promise]",
        "[object Promise] 2-0 [object Promise]",
]

CodePudding user response:

If you use await to wait for promises instead of using then, it might be easier to read (and write).

const res = await axios.get(`xxxx`)
const scores = res.data.data;

Word of warning - you didn't explain where you intend to run this code, and what your build process is (if any). await isn't always available without using a tool like Babel or esbuild to convert modern JavaScript to JavaScript that's compatible with a wide range of browsers.

However, assuming you're running your code in an environment that supports async / await, you can also make use of the Promise.all function to wait for all 3 promises to complete.

async function fetchFullTimeHeadToHeadScore(homeTeamId, awayTeamId) {
    const homeTeamNamePromise = fetchTeamName(homeTeamId);
    const awayTeamNamePromise = fetchTeamName(awayTeamId);
    const scorePromise = fetchScore(homeTeamId, awayTeamId); // axios.fetch
    const results = await Promise.all([ homeTeamNamePromise, awayTeamNamePromise, scorePromise ]);
    const homeTeamName = results[0];
    const awayTeamName = results[1];
    const matches = results[2];
    return matches.map(match => `${homeTeamName} ${match.scores.ft_score} ${awayTeamName}`);
}

If you really need to get this working with promises, this article explains: https://javascript.info/promise-chaining

Here's the relevant code:

// Make a request for user.json
fetch('/article/promise-chaining/user.json')
  // Load it as json
  .then(response => response.json())
  // Make a request to GitHub
  .then(user => fetch(`https://api.github.com/users/${user.name}`))
  // Load the response as json
  .then(response => response.json())
  // Show the avatar image (githubUser.avatar_url) for 3 seconds (maybe animate it)
  .then(githubUser => {
    let img = document.createElement('img');
    img.src = githubUser.avatar_url;
    img.className = "promise-avatar-example";
    document.body.append(img);

    setTimeout(() => img.remove(), 3000); // (*)
  });

In your case, it looks like you can slot in a Promise.all([ homeSide, res.data.data, awaySide ]); into your code to return the resolved promises for homeSide and awaySide, and the actual value of res.data.data (even though it's not a promise).

function fetchFullTimeHeadToHeadScore(homeTeamId, awayTeamId) {
  return axios
    .get(
      `https://soccer.sportmonks.com/api/v2.0/head2head/${homeTeamId}/${awayTeamId}?api_token=${apiKey}`
    )
    .then((res) => {
      const homeSide = fetchTeamName(homeTeamId)
      const awaySide = fetchTeamName(awayTeamId)
      return Promise.all([ homeSide, res.data.data, awaySide ]);
    })
    .then((data) => {
      return data[1].map((obj) => {
        return data[0]   " "   obj.scores.ft_score   " "   data[2];
      });
    })
    .then((scores) => {
      scores.length = 10;
      return scores;
    })
    .catch((err) => {
      console.log(err);
    });
}

CodePudding user response:

fetchTeamName returns an unresolved Promise, whose return value is only accessible within the success function (.then(onFulfillment, onRejection)). I think you've recognised this, which is why you've done fetchTeamName(foo).then((res) => res), but the misconception is that you haven't realised that this also returns a Promise!

There's no way to access the result of a Promise outside the scope of its instance methods (.then(), .catch(), etc.).

You could probably do something like

      fetchTeamName(homeTeamId).then((homeSide) => {
        fetchTeamName(awayTeamId)
          .then((awaySide) => {
            return res.data.data.map((obj) => {
              return `${homeSide} ${obj.scores.ft_score} ${awaySide}`;
            });
          })
          .then((scores) => {
            scores.length = 10;
            return scores;
          });
      });

but you're probably thinking "hang on, the value of awaySide doesn't depend on the value of homeSide, so why am I waiting for that API call to complete before I even start the call to get the value of awaySide"?

You'd be right to think this! A better solution uses Promise.all:

      const homeSidePromise = fetchTeamName(homeTeamId);
      const awaySidePromise = fetchTeamName(awayTeamId);

      Promise.all([homeSidePromise, awaySidePromise])
        .then(([homeSide, awaySide]) => {
          return res.data.data.map((obj) => {
            return `${homeSide} ${obj.scores.ft_score} ${awaySide}`;
          });
        })
        .then((scores) => {
          scores.length = 10;
          return scores;
        });

This is better, but it's still hard to read and not much better than the callback hell that you used to get before Promises were a widely-supported language feature.

A more modern pattern involves using async/await. This syntax still uses Promises "under the hood", but helps you write code that is much more readable. I'll leave rewriting your code sample with async/await as an exercise for the reader.

CodePudding user response:

It looks like you are receiving unhandled promises.

I think you could use Async and await - what this will do is stop your code from compiling when you send your get request. Making the code wait until the request is fulfilled, until it continues to compile.

This is a link to an article that could help you get started with it :)

https://dev.to/tutrinh/basic-using-async-and-await-with-axios-ad5

[edit]

This is a rough idea what it could look like - hope this helps somewhat.

const fetchTeamName = async(teamId) => {
    try{
        const response = await axios.get(`https://soccer.sportmonks.com/api/v2.0/teams/${teamId}?api_token=${apiKey}`)
        return response.data.data.name
    }  
}

CodePudding user response:

as someone has already mentioned; axios returns a promise. Promises are a fundamental part of javascript. You should read https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise . The following for lines are causing the problem (this is not a problem; its just how javascript behaves)

      const homeSide = fetchTeamName(homeTeamId).then((res) => {
        return res;
      });
      const awaySide = fetchTeamName(awayTeamId).then((res) => {
        return res;
      });

to overcome this you can wrap the function calls in async. I.e

async function fetchFullTimeHeadToHeadScore(...){...}

and then use await

      const homeSide = await fetchTeamName(homeTeamId)
      const awaySide = await fetchTeamName(awayTeamId)

It's better to get an understanding of promises first before using the solution. Good luck

  • Related