1

I am trying to fetch data from an API but keep getting undefined as result. I am using promise.all to chain API calls, and then chaining promises to do other operations on data. I seem to be unable to get my results to be passed to state.

Here's the offending code:

if (gameDifficulty === "mixed") {
  const result = await Promise.all([
    fetchClient.getData(
      getEndpoint(
        `amount=${countValues[0]}&difficulty=easy&type=${questionType}`
      )
    ),
    fetchClient.getData(
      getEndpoint(
        `amount=${countValues[1]}&difficulty=medium&type=${questionType}`
      )
    ),
    fetchClient.getData(
      getEndpoint(
        `amount=${countValues[2]}&difficulty=hard&type=${questionType}`
      )
    ),
  ])
    .then(function (responses) {
      console.log(responses);
      console.log(result); //returns undefined
      return Promise.all(
        responses.map(function (response, idx) {
          return fetchClient.processData(response, "results");
        })
      );
    })
    .then(function (data) {
      console.log(data);

      console.log(result); // returns undefined
      return [...data[0], ...data[1], ...data[2]];
    });
}
Ali Esmailpor
  • 1,209
  • 3
  • 11
  • 22
Eke Eke
  • 25
  • 10
  • You're mixing `async/await` with `.then` code, don't do that. Just use one of the other. Also why would you even need `result` in that `.then`, you already have `responses` which would be the same thing - the responses from the API requests – Jayce444 Jan 22 '21 at 03:55
  • It was an attempt at tracking the problem. I wanted to see what would get returned. The other console logs returned the expected result but the entire function returns undefined. I assume it's an asynchronicity thing. – Eke Eke Jan 22 '21 at 04:04

4 Answers4

1

Make it simple with async/await syntax.

Avoid mixing async/await with then/catch style.

const getDataResponses = await Promise.all([
  fetchClient.getData(
    getEndpoint(
      `amount=${countValues[0]}&difficulty=easy&type=${questionType}`
    )
  ),
  fetchClient.getData(
    getEndpoint(
      `amount=${countValues[1]}&difficulty=medium&type=${questionType}`
    )
  ),
  fetchClient.getData(
    getEndpoint(
      `amount=${countValues[2]}&difficulty=hard&type=${questionType}`
    )
  ),
]);

const data = await Promise.all(
  getDataResponse.map((response) => {
    return fetchClient.processData(response, "results");
  })
);

console.log('getDataResponses', getDataResponses);
console.log('processDataResponse', data);
return [...data[0], ...data[1], ...data[2]];
hoangdv
  • 15,138
  • 4
  • 27
  • 48
  • Thank you very much. I tried this and got the same result , but at least it's better formatted and written so that's great. The processDataResponse console log returns a promise . It seems that it doesn't get passed down to be concatenated in the last line. – Eke Eke Jan 22 '21 at 04:30
  • @EkeEke Thank for quick response, I updated my answer, missing `await` keyword at `const data = Promise.all` => `const data = await Promise.all` – hoangdv Jan 22 '21 at 04:32
  • It worked! Thank you very much. This betrays a lack of understanding of asynchronicity on my part. I'm curious though, why is it unadvisable to pair async/await with then/catch syntax? Does it lead to weird edge cases or will the problem mostly come from human error? – Eke Eke Jan 22 '21 at 04:37
  • It requires deep knowledge about async process in js. And it just mix them when you make sure that you need them. – hoangdv Jan 22 '21 at 04:52
0

in your browser Goto your network tab send a api request and see if it is sending any pre-flight request ?

the pre-flight request will have method of OPTIONS so you will identify it

Bhumit 070
  • 416
  • 4
  • 12
  • I can't seem to find the OPTIONS part but my console logs return the values from the calls, if that's any help. I checked the network tab. – Eke Eke Jan 22 '21 at 04:10
  • you will se all of your api calls and the reslut of api calls in your network tab so go there and check wether your api returns undefined or not – Bhumit 070 Jan 22 '21 at 04:17
  • Oh right, the actual API calls return their values, it's the chained promises that aren't working as expected. The code carries on without the results and breaks. – Eke Eke Jan 22 '21 at 04:32
0

Since result gets values directly from await on Promise you can use that instead of .then().

const responses = await Promise.all([
    fetchClient.getData(
        getEndpoint(
            `amount=${countValues[0]}&difficulty=easy&type=${questionType}`
        )
    ),
    fetchClient.getData(
        getEndpoint(
            `amount=${countValues[1]}&difficulty=medium&type=${questionType}`
        )
    ),
    fetchClient.getData(
        getEndpoint(
            `amount=${countValues[2]}&difficulty=hard&type=${questionType}`
        )
    ),
])

console.log('responses', responses);

const data = await Promise.all(
    responses.map(function (response, idx) {
        return fetchClient.processData(response, "results");
    })
);

console.log('data', data);
console.log([...data[0], ...data[1], ...data[2]]);
Sridhar
  • 11,466
  • 5
  • 39
  • 43
  • 1
    I have done this - @hoangdv suggested the same above, and have resolved the issue. Thank you very much. I don't have a high enough reputation for my upvote to reflect but I have marked as useful. Thanks again. – Eke Eke Jan 22 '21 at 04:42
0

Other answers show how to correct the code while avoiding mixing promise chains and await operator usage. This answer is about why the undefined value of result occurs:

 const result = await <promise chain>

creates a binding for result before executing the await operand. Like all simple variable definitions it starts out with a value of undefined, and because it now exists, accessing result in later code will not generate errors.

The object value of a promise chain is the promise returned by the last promise method (namely then, catch or finally) called in the chain. This is the promise that the await operand is waiting to become settled before returning its (successful) value as the value of the await expression.

Hence await does not return a value to be assigned to result until after the last then handler returns, meaning after the anonymous function in

.then(function (data) {
  console.log(data);

  console.log(result); // returns undefined
  return [...data[0], ...data[1], ...data[2]];
});

has finished execution and returns. Until then, all the console.log(result) calls log undefined because no assignment to result has taken place.


TLDR;

Handlers attached to promises are executed asynchronously, in a fresh call out from the event loop, after the promise they are attached to becomes fulfilled or rejected.

await is a unary operator with the ability to save execution state and return to the event loop until its promise operand becomes settled.

The order of operation of the posted code is

  1. define result, initialize it to undefined and set up the await operator to obtain the result of the promise returned by then in step 5.

  2. Issue three requests for endpoint data.

  3. Use Promise.all to get an array of responses from step 2.

  4. (then) use Promise.all to asynchronously get an array of process data for each response in the array from the result of step 3.

  5. (then) flatten the arrays of data from step 4 and return the flattened array

  6. (then) the await operator gets a callback with the result of step 5 and returns it as the value of the await expression

  7. the value returned from step 6 is used to initialize the const result declaration. Syntactically this occurs before the end of the const result statement, but in actuality has spread out over the time required for multiple network requests and call outs from the event loop.

traktor
  • 17,588
  • 4
  • 32
  • 53
  • Those console.logs were a hapless attempt at finding where the problem was. Thanks for the detailed explanation. I'd still love to find out why the admittedly clunky code I wrote kept returning undefined after running, so I can better troubleshoot if I ever see it next time. Any thoughts on that? Thank you very much. – Eke Eke Jan 22 '21 at 05:48