wait for one fetch to finish before starting the next

Tags: , , ,



I have a list of data that I am sending to google cloud. My current code looks like this:

const teams = ['LFC', 'MUFC', 'CFC'];

teams.forEach(team => {
    fetch({
      url: URL,
      method: 'PUT',
      body: team
    });
})

This works with one team but it is timing out if sending multiple files and the files are bigger. I am sending images over and not strings. To solve this I need to POST the data one file by one, and wait for the previous POST to complete before sending the subsequent one. Can anyone advise the best way of doing this?

Worth noting that I don’t have any control over the number of files that are uploaded.

Answer

Use a reduce instead of forEach, with .then().

The following will store the promise of the last fetch in acc (the accumulator parameter of reduce), and appends the new fetch inside of a then listener, to ensure that the previous fetch is finished:

const teams = ['LFC', 'MUFC', 'CFC'];

teams.reduce((acc, team) => {
  return acc.then(() => {
    return fetch({
      url: URL,
      method: 'PUT',
      body: team
    });
  })
}, Promise.resolve())
  .then(() => console.log("Everything's finished"))
  .catch(err => console.error("Something failed:", err))

//Simulate fetch:
const fetch = team => new Promise(rs => setTimeout(() => {rs();console.log(team)}, 1000))

const teams = ['LFC', 'MUFC', 'CFC'];

teams.reduce((acc, team) => {
  return acc.then(() => {
    return fetch({
      url: URL,
      method: 'PUT',
      body: team
    });
  })
}, Promise.resolve())
  .then(() => console.log("Everything's finished"))
  .catch(err => console.error("Something failed:", err))

You can even write a general helper function for it:

const teams = ['LFC', 'MUFC', 'CFC'];

const promiseSeries = cb => teams.reduce((acc, elem) => acc.then(() => cb(elem)), Promise.resolve())

promiseSeries((team) => {
  return fetch({
    url: URL,
    method: 'PUT',
    body: team
  })
})
  .then(() => console.log("Everything's finished"))
  .catch(err => console.error("Something failed:", err))

//Simulate fetch:
const fetch = team => new Promise(rs => setTimeout(() => {rs();console.log(team)}, 1000))

const teams = ['LFC', 'MUFC', 'CFC'];

const promiseSeries = cb => teams.reduce((acc, elem) => acc.then(() => cb(elem)), Promise.resolve())

promiseSeries((team) => {
  return fetch({
    url: URL,
    method: 'PUT',
    body: team
  })
})
  .then(() => console.log("Everything's finished"))
  .catch(err => console.error("Something failed:", err))

Or, even better, if you can (it’s ES2017), use async/await (it’s more readable):

const teams = ['LFC', 'MUFC', 'CFC'];

async function upload(teams){
  for(const team of teams){
    await fetch({
      url: URL,
      method: 'PUT',
      body: team
    });
  }
}

upload(teams)
  .then(() => console.log("Everything's finished"))
  .catch(err => console.error("Something failed:", err))

//Simulate fetch:
const fetch = team => new Promise(rs => setTimeout(() => {rs();console.log(team)}, 1000))

const teams = ['LFC', 'MUFC', 'CFC'];

async function upload(teams) {
  for (const team of teams) {
    await fetch({
      url: URL,
      method: 'PUT',
      body: team
    });
  }
}

upload(teams)
  .then(() => console.log("Everything's finished"))
  .catch(err => console.error("Something failed:", err))


Source: stackoverflow