PrestonDocks
PrestonDocks

Reputation: 5426

how to use promises in nested function calls

Just as I thought I understood promises... I have commented my code below where I am struggling, but in essence, I just can't understand why when the code below is run I get the following result

console.log("Finished Processing sheets")
console.log("Processing Errors")
console.log("sheet has completed processing")

When I would have expected

console.log("sheet has completed processing")
console.log("Finished Processing sheets")
console.log("Processing Errors")

Here is the code.

module.exports = async function(file) {
var sheets = await readXLSX(file, { getSheets: true })

  await Promise.each(Object.values(sheets), (sheet)=>{
    readXLSX(file, { sheet }).then((data)=>{
      //Process sheet data here
      return processSheet(data)
    })
  })
  console.log("Finished Processing sheets")
  console.log("Processing Errors")

}
var processSheet = async function(data){
//Do some processing and write excel data to database
await db.table('some_table')
.insert(.....)

return new Promise((resolve, reject)=>{
console.log("sheet has completed processing")
resolve()
})
}

Upvotes: 1

Views: 359

Answers (2)

Bergi
Bergi

Reputation: 665536

Your problem is that your Promise.each callback doesn't return the promise, so the loop cannot wait for it.

Promise.each(Object.values(sheets), (sheet)=>{
  return readXLSX(file, { sheet }).then(processSheet)
//^^^^^^
})

Or with async/await:

module.exports = async function(file) {
  const sheets = await readXLSX(file, { getSheets: true })

  await Promise.each(Object.values(sheets), async (sheet) => {
//                                          ^^^^^
    const data = await readXLSX(file, { sheet })
    return processSheet(data)
  })
  console.log("Finished Processing sheets")
  console.log("Processing Errors")
}

async function processSheet(data) {
  await db.table('some_table').insert(…)
  console.log("sheet has completed processing");
  return undefined; // no need to construct a promise here
}

Upvotes: 1

Kaddath
Kaddath

Reputation: 6151

I assumed readXLSX returns a Promise because you use .then() on it. then returns a Promise so if you want Promise.each to work as expected, you should do return readXLSX(...).then(...);

Here is an example with your code simplified:

//Promise.each emulation
Promise.each = function(arr, fn) {
  if(!Array.isArray(arr)) return Promise.reject(new Error("Non array passed to each"));
  if(arr.length === 0) return Promise.resolve(); 
  return arr.reduce(function(prev, cur) { 
    return prev.then(() => fn(cur))
  }, Promise.resolve());
};

//readXLSX emulation
var readXLSX = function(val){
    return new Promise(function(resolve, reject){
        setTimeout(function() {
            resolve(val);
        }, 1);
    });
};

var test = async function(file) {
  var sheets = { foo: 'bar', foo2: 'bar2' }

  await Promise.each(Object.values(sheets), (sheet)=>{
      return readXLSX(sheet).then((data)=>{
          //Process sheet data here
          return processSheet(data)
      });
  })
  console.log("Finished Processing sheets")
  console.log("Processing Errors")

};
var processSheet = async function(data){
  return new Promise((resolve, reject)=>{
    console.log("sheet has completed processing", data)
    resolve()
  })
};

test();

Upvotes: 1

Related Questions