1

I'm looking to iterate over a number of promises but the lambda function is completing before they are resolved.

module.exports.handler= async(event, context, callback) => {
  let a = {'a': 'b', 'x', 'y'};
  let b = {'i': 'n'};

  Object.keys(listA).map(async ax => {
    Object.keys(listB).map(async bx => {
      await validate(ax, bx);
    }
  }
}

async function validate(a, b) {
  let promise = getPromise(a, b);
  await promise.then((output) => {
    ...
    console.log('success');
  });
}

How can all the promises be resolved before the process completes?

Matthew
  • 385
  • 1
  • 2
  • 13
  • I have personally never seen using `await` and the `.then` of a promise in this way. It may be better to do something like: `const output = await promise;` and then do what you need to do. – Evan Bechtol Dec 09 '19 at 17:10
  • It depends. Are any of the steps in the iteration dependent on the previous ones? Or do you just want to be notified when they're all done? What about errors? Do you want the whole thing to keep truckin or just crash and burn? – Jared Smith Dec 09 '19 at 17:15

2 Answers2

4

This is because awaits in loops that require a callback will not be processed synchronously (See this).

One way you could avoid this is you could build an array of promises, and use Promise.all to await completion.

Example:

module.exports.handler = (event, context, callback) => {
    let a = {'a': 'b', 'x': 'foo', 'y': 'bar'};
    let b = {'i': 'n'};

    let promises = []
    Object.keys(a).forEach(ax => {
        Object.keys(b).forEach(bx => {
            promises.push(validate(ax, bx));
        })
    })

    Promise.all(promises)
        .then(results => {
            //do stuff with results
        })
        .catch(error => {
            //handle error
        })
}
Michael Rodriguez
  • 2,142
  • 1
  • 9
  • 15
  • It's worth noting that with `promise.all` any failed promise will short-circuit this logic; none of the other promises will be handled. – Evan Bechtol Dec 09 '19 at 17:11
  • 2
    There is no need to add `await` before `Promise.all` as you are already using `.then`. As well you can chain `catch`, so if any error occur it gets catched. – random Dec 09 '19 at 17:14
  • Indeed @randomSoul I have updated my answer, thanks for pointing that out. – Michael Rodriguez Dec 09 '19 at 17:39
  • 1
    Also neither of the callbacks need to be `async` or `.map()`... would be better to remove the `async` keyword and use `forEach()` instead. – Patrick Roberts Dec 09 '19 at 17:41
  • 1
    For reasons I'm not quite sure of, I needed to use: await Promise.all(...) for it to work in AWS lambda although it would work locally without it. Thanks for the help. – Matthew Dec 10 '19 at 14:25
3

The Promise.allSettled() method returns a promise that resolves after all of the given promises have either resolved or rejected, with an array of objects that each describes the outcome of each promise.

you can use @Michael solution just replace Promise.all with Promise.allSettled

The Promise.allSettled() method returns a promise that resolves after all of the given promises have either resolved or rejected, with an array of objects that each describes the outcome of each promise mozilla Doc.

The Promise.all() method returns a single Promise that fulfills when all of the promises passed as an iterable have been fulfilled or when the iterable contains no promises. It rejects with the reason of the first promise that rejects mozilla doc.

module.exports.handler = (event, context, callback) => {
    let a = {'a': 'b', 'x': 'foo', 'y': 'bar'};
    let b = {'i': 'n'};

    let promises = []
    Object.keys(a).forEach(ax => {
        Object.keys(b).forEach(bx => {
            promises.push(validate(ax, bx));
        })
    })

    Promise.allSettled(promises)
        .then(results => {
            //do stuff with results
        })
        .catch(error => {
            //handle error
        })
}

const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const promise3 = Promise.resolve(4);
const promises = [promise1, promise2,promise3];

Promise.allSettled(promises).
  then((results) => results.forEach((result) => console.log(result.status)));

// expected output:
// "fulfilled"
// "rejected"
// "fulfilled"
Jadli
  • 858
  • 1
  • 9
  • 17