Implement an async Angular validator with HTTP and PENDING feedback

Tests Angular async validator lifecycle and HTTP race conditions. A strong candidate returns an Observable from AsyncValidatorFn, lets Angular set PENDING automatically, debounces input, and binds control.pending in the template.
What's really being asked
Whether the candidate understands the Angular Reactive Forms async validation contract, the AbstractControl status lifecycle, and how to prevent HTTP race conditions and memory leaks during user input validation. It also checks if they know the difference between updateOn change and blur and how that impacts backend load. This question separates engineers who have built production-grade forms from those who only use built-in required validators.
The full answer
First, implement AsyncValidatorFn and return an Observable of ValidationErrors or null, or a Promise. Angular automatically sets the control status to PENDING when async validation starts and resolves it to VALID or INVALID when the returned stream completes or the Promise settles. Second, never manually call markAsPending because the framework manages this state internally. Third, debounce the input to avoid request storms, usually by piping debounceTime inside the returned Observable so rapid keystrokes cancel the prior in-flight validation via Angular's internal unsubscribe behavior. Fourth, expose the pending state to the template with control.pending to render a loading indicator or disable submission. Fifth, ensure the Observable eventually emits and completes, because Angular waits for the first emission to resolve the status and will leave the control in PENDING otherwise.
The mistakes people make
Calling control.markAsPending manually inside the validator instead of trusting Angular to handle it. Returning a plain object or synchronously throwing rather than returning an Observable or Promise. Subscribing to the HTTP call inside the validator with subscribe and not returning anything, which breaks Angular's cancellation mechanism and leaks memory. Failing to debounce so the backend receives a request on every keystroke. Checking control.status in the component TypeScript to toggle UI state instead of binding directly to pending in the template, which adds unnecessary change detection complexity.
What usually comes next
How do you handle server errors during validation? The candidate should catch the error and map it to a validation error object or rethrow to avoid breaking the form. What happens if the user navigates away while validation is pending? Angular's internal subscription is tied to the control lifetime, so destroying the component cleans it up automatically. How do multiple async validators interact? Angular runs them in parallel when provided as an array to the async validators argument. Should you use a Promise or Observable? Observables are preferred because Angular can unsubscribe from them when the value changes, while Promises cannot be cancelled and may deliver stale results.
A concrete example
Inject HttpClient into a UsernameValidator class implementing AsyncValidator. The validate method returns of(control.value).pipe(debounceTime(300), distinctUntilChanged(), switchMap(value => this.http.get(/api/check-username/${value})), map(isTaken => isTaken ? { usernameTaken: true } : null), catchError(() => of(null))). In the component template, show a spinner when form.get(username).pending is true and disable the submit button until the control status is VALID.
Interview question
Which approach correctly implements an AsyncValidatorFn that performs an HTTP username check while minimizing backend load and preventing race conditions?
- a.Return an Observable that uses debounceTime and switchMap, letting Angular set PENDING and bind control.pending in the templateCorrect
- b.Subscribe to the HTTP call inside the validator, call markAsPending on the control, and update a component property to show the loading state
- c.Return the HTTP Observable directly without debounceTime so every keystroke triggers an immediate, cancelable request
- d.Return a Promise from the HTTP call and manually set the control to PENDING with markAsPending until it resolves
Why? this is the answer
Returning a debounced Observable with switchMap allows Angular to automatically manage the PENDING status, unsubscribe from prior in-flight requests when the value changes, and avoid memory leaks. Option B breaks cancellation by subscribing internally and wrongly manages state imperatively, while C uses an uncancelable Promise and manual pending, and D floods the backend by skipping debounce.
Just read this? Test yourself on what you have been reading.
Read the original → angular.dev
You just looked this up. Could you explain it out loud?
That is the part interviews actually test. Tezvyn takes questions like this one and gives you what the interviewer is really checking, the answer that lands, and the mistake that ends the conversation, in the four minutes before your next meeting.
The iPhone app is on the way
We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.
Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.
We are hiring for this. Open roles that interview on angular — each one lists the topics its interview covers.
See open roles