Company: Tiktok
Role: Senior FE engineer
Candidate: Yoe 8
Technical Phone screen: 2 questions.
Overview:
- Pretty typical FE screen. Interviewer didn’t provide any examples, so I had to create them myself which was a bit strange. Usually theirs a input output example, and I wasted time hear coming up with one.
- Interviewer first language wasnt english too, so felt we wasted time understanding each other.
I didn’t pass, because I didn’t get the trailing option in debounce working.
Question 1: Valid Parens
// Example1:
// Input: `()`
// Output: true (balanced)
// Example2:
// Input: `()[]`
// Output: false
// Example2:
// Input: `(()}`
// Output: true (balanced)
// Example3:
// Input: `((`
// Output: false
// Example4:
// Input: `()(`
// Output: false
Question 2: Debounce function
- part 1: typical debounce
- part 2: add support for immediate option
Follow-up: Support Immediate (Leading Edge) Execution
Extend debounce with an immediate flag. When true, it fires on the leading edge (first call), then ignores subsequent calls until the caller has been silent for delay ms.
Ex: of immediate flag usage (given by interviewer later)
function debounce(func:Function, ms:number, immediate=false){
let timeoutId:any = null;
function debounced(...args:any){
let shouldCallImmediately = immediate === true && timeoutId === null;
clearTimeout(timeoutId)
timeoutId = setTimeout(() => {
timeoutId = null;
if(immediate === false){
func.apply(this, args)
}
}, ms)
if(shouldCallImmediately){
func.apply(null, args)
}
}
// Improvements.
debounced.cancel = () => {
clearTimeout(timeoutId);
timeoutId = null;
}
return debounced;
}
let j = 0;
function incrementJ(label:string) {
j++;
console.log(label, 'j:', j);
}
// leading only
const debouncedIncLeading = debounce(incrementJ, 200, true);
// Burst 1
debouncedIncLeading('burst1-call1'); // t = 0ms → should fire immediately ✅ and i is 1
debouncedIncLeading('burst1-call2'); // t ≈ 50ms → ignored
debouncedIncLeading('burst1-call3'); // t ≈ 100ms → ignored
setTimeout(() => {
console.log('After 100ms, j is:', j);
}, 100);
// Wait long enough (silence ≥ delay), then Burst 2
setTimeout(() => {
debouncedIncLeading('burst2-call1'); // t ≈ 400ms → new first call, fires since timeoutID finally reset to null, ✅ and i is 1
debouncedIncLeading('burst2-call2'); // t ≈ 450ms → ignored
}, 400);
setTimeout(() => {
console.log('After 1 second, final j is:', j);
}, 1000);
/*
if we call immediate the first time, the setTimeout gets created
then in burst1-call2' we reset clearTimeout
then in burst1-call3' we reset clearTimeout
for burst2-call, the timeoutId is finally null and thats why the immediate call
*/