-1

I've been trying to use a batched array and fire requests sequentially using Promise.all and a map function:

const slowAsync = (i) => {
  return setTimeout(() => {
     return Promise.resolve(i);
  }, 1000)
}

const batchedIds = [[1,2], [3,4], [5,6]];

batchedIds.map(async batch => {
  const response = await Promise.all(batch.map(id => {
    return slowAsync(id);
  }));
  
  console.log(response);
})

Right now the console logs everything at once

//after 1 second
[1, 2]
[3, 4]
[5, 6]

but I want it to log each resolve of the promise with 1 second difference

//after 1 second
[1, 2]
//after 1 second
[3, 4]
//after 1 second
[5, 6]

Is it possible to do this without recursion or libraries and by using a map function?

vaskort
  • 2,591
  • 2
  • 20
  • 29
  • I think that Promise.all inherently does the opposite of what you want, e.g. waits until all of the promises are resolved. You can do this sequentially with an await inside a loop. – Swiffy Dec 14 '21 at 20:56
  • 4
    You shouldn't be using `.map()` on your `batchedIds` array if you're not going to use the array the `.map()` creates for you, for that there is `.forEach()` - but both `.map()` and `.forEach()` don't await their asynchronous callbacks before proceeding to the next iteration, so if you want sequential behavior you can use a for..of loop with `await` (see: [Using async/await with a forEach loop](https://stackoverflow.com/a/37576787)) – Nick Parsons Dec 14 '21 at 20:57
  • thank you @NickParsons that was exactly my problem in my actual code, thanks for pointing this out! – vaskort Dec 14 '21 at 21:19

2 Answers2

2

Your slowAsync function is not correctly implemented. Returning something inside a setTimeout callback has no effect. And setTimeout itself returns a numerical identifier, not a promise.

Secondly, you should replace your .map() iteration with a for loop, and wrap all that in an async function so you can use await.

Here is the correction:

const slowAsync = (i) => new Promise(resolve =>
  setTimeout(() => resolve(i), 1000)
);

const batchedIds = [[1,2], [3,4], [5,6]];

(async () => {
  for (const batch of batchedIds) {
    const response = await Promise.all(batch.map(id => {
      return slowAsync(id);
    }));
    console.log(response);
  }
})();
trincot
  • 317,000
  • 35
  • 244
  • 286
0

@Trincot got it in a nutshell!

As a further remark: Instead of using a function closure you could also use Function.prototype.bind() to bind the individual ids of each batch to the setTimeout() callback-function like shown below:

const batchedIds = [[1,2], [3,4], [5,6],[7,8,9,10]];

(async () => {
  for (const batch of batchedIds) {
    console.log(await Promise.all(batch.map(id=>new Promise(r=>setTimeout(r.bind(null,id),1000)))));
  }
})();
Carsten Massmann
  • 26,510
  • 2
  • 22
  • 43