0

I am sending multiple requests to a API say "myapi/id". My ids could range from [0..10000]. Because sending all ids at once is very expensive, I would like to send in slice wait for it to be fetched and then send the next slice.

Here is the code I am using:

async function getSlice(data) {
    let promises = []
    data.map((key) => {
        promises.push(fetch("myapi/"+key)
        .then(res => res.json())
        .then((result) => console.log(result))) //I want to store the result in an array
    }
    await Promise.all(promises)
} 

function getAll(data) {
    for(let i=0; i<= data.length; i+=10) {
        getSlice(data.slice(i,i+10));
        //I want to wait for the previous slice to complete before requesting for the next slice
    }
}

getAll(ids)

However, the requests are being sent asynchronously/there is no waiting happening. I was wondering if there is an error in my code/ if there is any way to send multiple requests using a for loop and wait for them to complete before sending the next requests.

HereToLearn
  • 41
  • 1
  • 5
  • Is there any reason you would like to use `Promise.all` if you would like to call an API sequentially? (Other than saving data in an array) – James Yoo Apr 28 '20 at 23:39
  • So make another `Promise.map`? Instead of looping through `data`, map (a massaged version of ) `data` so that you can `await Promise.all(...)` that, too. Unless you need this to be synchronous (i.e. getSlice 0 needs to finish before getSlice 10 happens) in which case you may want to rethink why you're using async at all. – Mike 'Pomax' Kamermans Apr 28 '20 at 23:39
  • 1
    You want to chain promises synchronously. See this (the reduce trick) https://css-tricks.com/why-using-reduce-to-sequentially-resolve-promises-works/ Note: I would use forEach instead of map in your current setup – user3791775 Apr 28 '20 at 23:40
  • @JamesYoo I want to call it concurrently in a particular slice but call them sequentially from one slice to another. – HereToLearn Apr 28 '20 at 23:50

2 Answers2

1

You need to use await before async function if you want to wait for it for finish

async function getAll(data) {
    for(let i=0; i<= data.length; i+=10) {
        await getSlice(data.slice(i,i+10));
    }
}

getAll(ids).then(()=>console.log('all data processed')).catch(err=>/*handle 
error*/)

Ps. i think that you need to use Promise.allSettled method instead of Promise.all. If one of your request will return an error, you will get all chunk failed if you will use Promise.all. Promise.allSettled will wait for all results - positive or negative. Anothe old sollution is to use catch method for each request, like

 promises.push(fetch("myapi/"+key)
    .then(res => res.json())
    .then((result) => console.log(result))
    .catch((err)=>{/*handle error if needed*/; return null})

And after that you will have some nulls in array with results

Vladimir Mikhno
  • 167
  • 1
  • 5
1

An efficient way of doing is always keep the requests being called (limiting the maximum number of parallel requests) instead of the way you are doing.

By using your way (easier) it will always wait for everything to return then checks if there is more data in the array and call the function again recursively or return everything if it is done. Hope it helps.

async function getSlice(data) {
    let promises = []
    data.map((key) => {
        promises.push(fetch("myapi/"+key)
        .then(res => res.json())
        .then((result) => console.log(result))) //I want to store the result in an array
    }
    await Promise.all(promises)
} 

function getAll(data, index=0, length=10) {
    const allResponse = [];  
    return getSlice(data.slice(index,q)).then(response => {
        allResponse.push(response);
        newLength = length + 11 > data.length ? data.length : length + 11;
        if (index + 10 > data.length) //it's over
         return allResponse;
        return getAll(data, index + 10, length +11);
})}

Promise.all(getAll(ids).then(arrayResponse=>console.log('dowhatever',arrayResponse))
iwaduarte
  • 1,600
  • 17
  • 23