Search code examples
javascriptasync-awaites6-promiseaurelia

JS async/await doesn't wait for my functions to resolve their promise


Can you help me understand why this doesn't work:

I'm working in Aurelia, and I try to validate some input data using the validation controller, which is documented here: https://aurelia.io/docs/plugins/validation#validation-controller

Now, I have a map of this data, where I need to evaluate each entry, and I want to create an array, where each item contains the validation result of each entry, and when everything is done, return the array.

So something like this (simplified):

function ValidateAll(InputDataMap){
  let validationResults = new Array();

  InputDataMap.forEach((item) => {
     validationResults.push(validateEntry(item));
  });

 return validationResults;
}

function validateEntry(item){

(aurelia's validation)controller.validate(item, "some value", "some rule")
      .then(result => {
        return result;
      });
}

Now, this will not work of course, because I need to wait for the validation controller to resolve it's promise before I can get any data back, and so far I've failed at that.

I read that if you use the async/await keyword it will pause a function until the promise has been resolved, so I made changes, something like this:

function ValidateAll(InputDataMap){
      let validationResults = new Array();

      InputDataMap.forEach(async(item) => {

         let result = await validateEntry(item);
         validationResults.push(result);
});

Now, this doesn't work either, and that's what I'm wondering about. I suppose that my "validateEntry" function is considered finished by the "await" once it's run, and doesn't wait for the "validate()" function's promise inside "validateEntry" to be resolved. Can I write it as simply as this with a few modifications and still get it to work?


Solution

  • You have to return a Promise from your validateEntry:

    function validateEntry(item){
      return controller.validate(item, "some value", "some rule")
    }
    

    A then just returning its parameter is not required, and does nothing, so .then(result => { return result; }) can be removed.

    The async callback for the forEach would not make ValidateAll to wait for the validation. You have to wait for all Promises to be resolved, and return a Promise from ValidateAll, the forEach can be replaced by map so that you don't need to do the push manually:

    let validationResults = new Array();
    
    validationResults = InputDataMap.map(item => validateEntry(item));
    

    You don't need async here because you do not need an await here. Now validationResults contains a list of Promises. You now need to use Promise.all to wait until those are resolved.

    function ValidateAll(InputDataMap){
      let validationResults = InputDataMap.map(item => validateEntry(item));
    
      return Promise.all(validationResults);
    }
    

    Now ValidateAll will return a Promise that will resolve with an array containing the results of the validation.

    You could shorten the code even more to:

    function ValidateAll(InputDataMap){
      return Promise.all( InputDataMap.map(validateEntry) );
    }