Search code examples
javascriptasynchronousfetch-apies6-promise

Create a dynamic promise chain for multiple POST requests


I'm struggling to come up with a dynamic solution, I have an array of items, I need to send a POST request for each item in the array, however I do not want to send the next request until the previous promise is resolved.

So dynamically chaining .then() methods, i.e. send the first POST request, wait for that to get resolved before sending the second POST request, wait for second to get resolved before sending third POST request, etc.

Here is my static code, where I send a POST request for the first item in the commandPayloads array, then send a POST request for the second item after the first is resolved.

const commandPayloads = await createInitialCommands(copiedCommands);

  commandPOSTFetch(commandPayloads[0], newResponseSetID).then(() => {
    commandPOSTFetch(commandPayloads[1], newResponseSetID);
  });

const commandPOSTFetch = async (command, newResponseSetID) => {
  const res = await fetch(`https://${someWebSite}/${appID}//${interactionID}/response/${newResponseSetID}/commands.json`,
    {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify(command),
    }
  );
  const data = await res.json();
  return data;
};

the example above is working if there are 2 items in the array, but what if I have more than 2?

How can I make this logic dynamic, based off the number of items in the array, so the POST requests are sent in sequential order, only after the previous is resolved?

*Update with For Loop

  commandPayloads.forEach(async (command, i) => {
    const data = await commandPOSTFetch(command, newResponseSetID)
    console.log('in loop', data);
  });

Solution

  • Just for the record, it turns out that the simplest thing to do (and, really, given the "wait until one thing finishes before doing the next thing" requirement, almost the only way to do it) is to use a good old-fashioned for loop:

    let listOfThings = whatever;
    for (let i = 0; i < listOfThings.length; ++i)
        const asyncResult = await somefunc(listOfThings[i]);
        // ...
    }
    

    The reason a for loop is better in this case than .forEach() or even .map() is that in an async function, those await expressions really will wait until each Promise is satisfied before proceeding to the next iteration. (Obviously in real code you'd deal with exceptions etc.)

    Sometimes you don't need to sequence things. In such cases, using .map() to collect a bunch of Promise objects that can then used with await Promise.all(collected) is great. But when you do want a strict one-before-the-other sequence, a plain loop is the best bet.