-1

I am trying to make multiple API calls to OpenAI in node.js to answer multiple questions at once. To do this I am using an async function with promise.all and then calling await on the API request (using request). In theory this should wait until all API calls are complete before then returning the response but the res.json response is called async rather than waiting so it's responding with an empty array.

router.get('/multiplequestions', (req, res, next) => {
    console.log("hit");
    const questions = ["How long is a metre in feet", "What is 10 + 12", "How many miles to the moon"]; // Array of ids
    const answers = [];
    const start = async () => {
        const answersResponse = await Promise.all(questions.map(async question => {
            var options = {
                'method': 'POST',
                'url': 'https://api.openai.com/v1/completions',
                'headers': {
                    'Authorization': 'Bearer sk-xxx',
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({
                    "model": "text-davinci-003",
                    "prompt": question,
                    "temperature": 0.7,
                    "max_tokens": 2230,
                    "top_p": 1,
                    "frequency_penalty": 0,
                    "presence_penalty": 0
                })
            };
            await request(options, function(error, response) {
                if (error) throw new Error(error);
                const article = JSON.parse(response.body);
                const articleText = article.choices[0].text;
                answers.push(articleText)
                console.log(answers)
            });
        }));
    };
    async function wrap() {
        await start().then(res.json({
            answers
        }))
    }
    wrap();
});

The API calls work and return correctly, I just can't get it to wait before before res.json is sent.

I did read of course using an Async function means that it will be executed async but I thought the purpose of the await was so the code would wait until the api call is executed?

I thought wrapping start in another async function would work but runs the same, assume I've missed something to do with promises so pointers would be helpful?

EDIT

Here is the answer, now using fetch and returning correctly

router.get('/multiplequestions', (req, res, next) => {
  console.log("hit");

  const questions = ["How long is a metre in feet", "What is 10 + 12", "How many miles to the moon"]; // Array of ids
  
  const start = async () => {
   const answersResponses = await Promise.all(

            questions.map(async question => {

            let call = {
              "model": "text-davinci-003",
              "prompt": question,
              "temperature": 0.7,
              "max_tokens": 2230,
              "top_p": 1,
              "frequency_penalty": 0,
              "presence_penalty": 0

            }
          const response = await fetch('https://api.openai.com/v1/completions', {
          method: 'POST',
          body: JSON.stringify(call),
          'headers': {
            'Authorization': 'Bearer sk-xxx',
            'Content-Type': 'application/json'
                }
            });
            const data = await response.json();
            console.log(data);
            return data.choices[0].text
            })
        )
        res.json({answersResponses});
};
start();
});
user2389087
  • 1,692
  • 3
  • 17
  • 39
  • You have to `return` something from `.map()` if you want the answers after you've gotten them. – Pointy Feb 07 '23 at 15:31
  • 1
    Your `.map` callback doesn’t return anything explicitly, so the function returns a Promise that resolves to `undefined`, which will be resolved immediately in `Promise.all`. – Sebastian Simon Feb 07 '23 at 15:31
  • @SebastianSimon — That doesn't actually matter (the value of `answersResponse` is never used), so long as the promise resolves as `undefined` *after* the request is complete and the callback runs (which isn't the case here). – Quentin Feb 07 '23 at 15:32

1 Answers1

0

The key part of your problem lies here:

await request(options, function(error, response) {

You can only usefully await a promise.

request does not return a promise. See it's documentation. This is one reason that it was deprecated (marked as Do Not Use) three years ago.

Replace request with an API that is promise aware, such as fetch (which is now natively supported by Node.js).

(You could, instead, wrap request in a Promise constructor, but using a supported API is a much better option).


Aside: best practise would be to return the value from your HTTP request so that Promise.all resolved to be an array of the responses that you could use directly.

Mutating answers inside the array would work (if you delayed the promise resolution), but it isn't idiomatic or all that easy to understand.

Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335