Reputation: 3914
My question mentions an HTML form, but the crux of it pertains to how an arbitrary number of async JS functions can contribute to a single value – in the case of an HTML form, whether it's valid or not.
Being an old synchronous backend dinosaur, completely inexperienced with the use of JS promises/async-await, I can't seem to wrap my head around how to accomplish this.
Consider this validation class:
class Validation {
construct() {
this.validators = [];
}
register(validator) {
this.validators.push(validator);
}
validate(form) {
let valid = true; // Benefit of the doubt
this.validators.forEach(function(validator) {
valid = validator(form) ? valid : false;
});
return valid;
}
}
Say I want to register 3 validators:
const validation = new Validation();
validation.register(form => {
// Return true if all required form fields have values, false otherwise
});
validation.register(form => {
// Return true if the user has made any changes to the form, false otherwise
});
validation.register(form => {
// Do an AJAX request and return true/false depending on the response
});
And then check to see if the form is valid, disabling or enabling its submit button accordingly:
submitButton.setAttribute('disabled', !validation.validate(form));
All is fine for the first two validator functions, as they don't exhibit any asynchronous behavior. But the third validator function performs an AJAX call, and that is where it all falls apart.
I assume the answer to the problem lies along the lines of re-writing the whole validate() method to somehow call the validator functions in an asynchronous manner. Ensuring that the submit button is enabled only if all the validators have returned true (in their sweet time) is what's really baking my noodle.
Would someone be so kind as to magically transform my naive approach into something async friendly?
Upvotes: 1
Views: 1196
Reputation: 92304
Just await the validators in a loop, and you can probably exit early once you find an error. Your function must become async
.
Each validator can return a Promise<boolean>
or just a boolean
async validate(form) {
for (const validator of this.validators)
if (!(await validator(form)) {
return false;
}
}
return true;
}
// Somewhere else
const isValid = await validation.validate(form);
submitButton.setAttribute('disabled', !isValid);
Note that you cannot do it with Array#forEach
since it will not wait for each iteration if it returns a promise. You also cannot return early from an Array#forEach
Example of an async validator.
const validation = new Validation();
validation.register(form => {
return fetch("/validation")
.then(response => response.json())
.then(body => body.isValid)
});
Upvotes: 3