1

I have a an array of chunked data that I need to upload one chunk at time. The current implementation I used it to encapsulate the logic in an Promise.all() since I need to return the result of the promise, The problem with this approach is that all the upload is done asynchronously resulting in a Timeout error as the server can't process all the requests at the same time, How can I modify this method so that the upload is done one chunk at time ?.

My code:

var chunks = _.chunk(variableRecords, 30);
return Promise.all(
        chunks.map(chunk => this.portalService.updateDataForChart(variableId, chunk)))
        .then((updateRes: boolean[]) => {
          if (updateRes.every(updateStatus => updateStatus)) {
            return this.executeRequest<HealthDataSource, boolean>({
              path: `/variable/user/datasources/${dataSource.identifier}`,
              method: 'PUT',
              body: {
                libelle: dataSource.datasource.libelle,
                type: dataSource.datasource.type,
                lastSyncDate: Math.max(maxDate, dataSource.datasource.lastSyncDate)
              },
              headers: this.getHeaders()
            });
          } else {
            return false;
          }
        });

R0b0t0
  • 390
  • 6
  • 20
  • Possible duplicate of [Resolve promises one after another (i.e. in sequence)?](https://stackoverflow.com/questions/24586110/resolve-promises-one-after-another-i-e-in-sequence) – ponury-kostek Nov 29 '19 at 14:40

3 Answers3

5

You need them in SEQUENCE , for of is the way to go :

async function chunksSequence(chunks) {
  for(const chunk of chunks) {
    await // your other code here
  }
};

If you need to return something

async function chunksSequence(chunks) {
  let results = []
  for(const chunk of chunks) {
    let result = await // your other code here
    results.push(result)
  }
  return results 
};

Because of comment needed in a promise on return

async function chunksSequence(chunks) {
 return new Promise((resolve, reject)=>{
    let results = []
    for(const chunk of chunks) {
      let result = await // your other code here
      results.push(result)
    }
    resolve(results) 
  }
};
Renaldo Balaj
  • 2,390
  • 11
  • 23
2

You can do this with the help of Array.reduce()

const chunks = _.chunk(variableRecords, 30);
return tasks.reduce((promiseChain, currentTask) => {
    return promiseChain.then(chainResults =>
        currentTask.then(currentResult =>
            [ ...chainResults, currentResult ]
        )
    );
}, Promise.resolve([])).then(arrayOfResults => {
    // Do something with all results
});

Source : https://decembersoft.com/posts/promises-in-serial-with-array-reduce/

Weedoze
  • 13,683
  • 1
  • 33
  • 63
  • This assumes that `variableRecords` is an array of promises, wherein the process of requesting from the server has already been established. It will still have the same problem as the OP, as the server can't process all requests at the same time. – ryeballar Nov 30 '19 at 00:21
0

If you don't / can't use await you could use something like this

function runSequenceItem(chunks, index) {
 return new Promise(chunks[index])
  .then(res => {
     index ++
     if (index < chunks.length) {
       return runSequence(chunks[index], index + 1)
     } else {
       // this is not needed actually
       return 'done'
     }
   })
}

function runInSequence(chunks) {
 return runSequenceItem(chunks, 0)
}

If you also need the results then you can return an array at the end of the recursion runInSequence

    function runSequenceItem(chunks, index, results) {
 return new Promise(chunks[index])
  .then(res => {
     results.push(res)
     index ++
     if (index < chunks.length) {
       return runSequence(chunks[index], index + 1)
     } else {
       return results
     }
   })
}

function runInSequence(chunks) {
 return runSequenceItem(chunks, 0, [])
}

and then retrieve it at the end

let results = runInSequence(chunks)
Radu Diță
  • 13,476
  • 2
  • 30
  • 34