Asynchronous Validation
Async validations are throttled like sync validations, and changes made while one is running are queued.
Behaviors:
- Jobs are throttled like sync validators (default 100ms delay)
- Changes during a running validation are queued, not aborted: The running handler completes, and the latest value is validated after it settles. If the change's delay has already passed by then, it is validated
delayMsafter the handler settles; otherwise, when its delay ends. Intermediate values are skipped - The result of the running validation is currently applied when it settles, so an error for the earlier value may show briefly until the latest value has been validated
- The
abortSignalis aborted when the validator is reset (validator.reset()) or the handler is removed (by calling the function returned fromaddAsyncHandler()ormakeValidatable()). The result of an aborted run is discarded, so errors it collected are not applied, even if the handler ignores the signal - Use the signal with
fetch()and other async APIs to cancel in-flight requests in those cases
Error handling:
- Errors thrown in async handlers are logged to the console but don't break validation
- The validation state transitions continue normally even when errors occur
- This ensures one buggy validator doesn't break the entire validation system
class UserModel { @observable username = "";
constructor() { makeObservable(this);
makeValidatable( this, () => this.username, async (username, builder, abortSignal) => { // The signal is aborted by validator.reset() or by removing the handler const response = await fetch(`/api/check-username/${username}`, { signal: abortSignal });
if (!response.ok) { builder.invalidate("username", "Username already taken"); } }, { initialRun: false } ); }}
const user = new UserModel();const validator = Validator.get(user);
runInAction(() => { user.username = "john";});runInAction(() => { user.username = "jane"; // Both changes fall within the same delay: only "jane" is validated});
// Check validation statevalidator.isValidating // truevalidator.reactionState // 1 (waiting for the delay)validator.asyncState // 0 (the job starts after the delay)
await when(() => validator.asyncState > 0); // "jane" is being validated
runInAction(() => { user.username = "jake"; // Does not abort "jane": "jake" is validated after it settles});
await validator.waitForValidation();
// The validation completed successfullyvalidator.isValid // true or false depending on the result