0

I have a "answersRequest" function, gets the id of the answers it writes to the "b" list

    const answersRequest = () => {
        let b = [];
        answers.map(answer => {
            axios({
                method: 'POST',
                url: 'http://127.0.0.1:8000/api/answers/',
                data: answer
            }).then(resp => {
                b.push(resp.data.id)
            })
        })
    }

And on completion of the map function, the below function needs to be run

const a = () => {setQuestionsBlok([...questionsBlok, {...questionBlokInputs, answers: b}]); setAnswers([])};

but I don't know how to find out when the map function ends

help me please

  • Does this answer your question? [Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference](https://stackoverflow.com/questions/23667086/why-is-my-variable-unaltered-after-i-modify-it-inside-of-a-function-asynchron) – SuperStormer Jul 16 '22 at 17:44

4 Answers4

1

You basically need to push the return values from the axios call to an array and then use Promise.allSettled() or Promise.all() to wait for all the responses before you can continue processing that data.

// Just faking an axios call for this example:
function mockedAxios({ data }) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({
        data: {
          id: Math.random().toString(36).substr(2, 9),
          result: data * 2,
        },
      });
    }, 1000 + Math.random() * 2000)
  });
}

async function answersRequest(answers) {  
  const promises = answers.map((answer) => {
    // Return each promise here:
    return mockedAxios({
      method: 'POST',
      url: 'http://127.0.0.1:8000/api/answers/',
      data: answer
    });
    // No need for a then block here, you can do it below in the `allSettled`'s `then`:
    // .then((resp) => {
    //   return resp.data.id;
    // });
  });
  
  // Wait for all responses and process the data:
  const IDs = await Promise.allSettled(promises).then((result) => {
    // result looks like { status: "fulfilled", value: { data: { id, result } } }
    return result.map(r => r.value.data.id);
  });

  return IDs;
}

async function update() {
  // You can now call and await this function from any other async function:
  const IDs = await answersRequest([1,2,3,4,5]);
  
  console.log(IDs);
  
  // And then, one you get the result, do whatever you need to do with it:
  // const a = () => {
  //   setQuestionsBlok([...questionsBlok, {...questionBlokInputs, answers: IDs }]);
  //   setAnswers([]);
  // };
}

update();
Danziger
  • 19,628
  • 4
  • 53
  • 83
0

You can use Promise.all to resolve all promises at once.

const answersRequest = () => Promise.all(
  answers.map(answer => 
    axios({
      method: 'POST',
      url: 'http://127.0.0.1:8000/api/answers/',
      data: answer
    })
  )
);

Promise.all takes all the Promises passed to it (in this case, they are the HTTP requests) and only resolves when all the Promises are resolved. So, it's sending all the HTTP requests and waiting for all of them to finish. After that, the value of the returned Promise is an array containing all the responses, which means you don't need a separate variable for b anymore.

Then you can use Promise.then to call the second function.

answersRequest().then(values => {
  setQuestionsBlok(
    [...questionsBlok,
      {
        ...questionBlokInputs,
        answers: values.map(response => response.data.id)
      }
    ]
  );
  setAnswers([]);
});

Here, values is an array containing all the responses from the requests. Then, the map call extracts all the id's from the response.

anut
  • 481
  • 1
  • 11
  • You probably should explain the changes you've made (why there's no `.push()` anymore, and how that "magic" implicit `return` takes over that job) – Andreas Jul 16 '22 at 17:51
  • Not really... ¯\\_(ツ)_/¯ – Andreas Jul 16 '22 at 18:00
  • i get in console error Unhandled Promise Rejection: TypeError: undefined is not a object (evaluation 'e.data') – Vahe Hakobjanyan Jul 16 '22 at 18:18
  • @VaheHakobjanyan that probably means that the response data is `undefined`. You may need to tweak the line that has `values.map`. – anut Jul 16 '22 at 18:58
0

At first you should enable asynchronous to your requests.

After that you can call the function at the end of the loop like this:

const answersRequest = () => {
    let i = 0; // Loop counter
    let b = [];

    // It's called at the end of requests
    const funcA = (b) => {
        setQuestionsBlok([
            ...questionsBlok,
            {...questionBlokInputs, answers: b}
        ])
        setAnswers([])
    }

    // Post answers request function
    const createAnswers = async (answer) => {
        return await axios({
            method: 'POST',
            url: 'http://127.0.0.1:8000/api/answers/',
            data: answer
        })
    }

    answers.map(answer => {
        i++; // Increase loop counter value
        createAnswers(answer).then(resp => {
            b.push(resp.data.id)

            // Runs specified function at the end of requests
            if(i === answers.length) {
                funcA(b)
            }
        })
    })
}
Morteza Rahmani
  • 516
  • 6
  • 7
-2

You can use async/await to prompt your function to pause execution until a step completes:

const answersRequest = async () => {
    let b = [];
    await answers.map(answer => {
        axios({
            method: 'POST',
            url: 'http://127.0.0.1:8000/api/answers/',
            data: answer
        }).then(resp => {
            b.push(resp.data.id)
        })
    })
    // execution pauses here
    setQuestionsBlok([...questionsBlok, {...questionBlokInputs, answers: b}]); setAnswers([])
}

In this manner, b will be defined as desired for the setQuestionsBlok call.

Jarmo
  • 191
  • 1
  • 10
  • 2
    Don't use `.map()` when you ignore it's return value. Also `await`ing an array of `undefined`s doesn't do anything useful. – Andreas Jul 16 '22 at 17:49