Here is my typical multi-part interview question.
Compute factorial of N using recursion
All I am looking for is simple code like this
1 | function factorial(n) { |
Can you verify this code computes the correct factorial?
I am looking for something quick and dirty
1 | console.assert(factorial(1) === 1, '1!'); |
If a person writes actual QUnit / Mocha / anything unit tests that is a plus.
Can you find the largest N the system can compute factorial for?
I am looking for the value of N where the max size of the stack is exhausted. The candidate should know that when using recursion the previous call remains on the stack, so computing factorial of 5 means there are five calls at once at some point
1 | factorial(5) { |
The candidate should try different values of N, probably starting with 1000, 10k, 100k, etc. On my machine, the stack is exhausted for n = 100k.
Can you write factorial using iteration?
Change code to a loop, and I am looking for the candidate to reuse the unit tests from before.
Can you write asynchronous version of factorial function using promises?
I am looking for recursive (simpler) version of the code, something like this using Q library
1 | var Q = require('q'); |
Can you write unit tests for your asynchronous factorial?
I am looking for something like this, quick and dirty, but the candidate should know how to handle the error.
1 | factorial(5).then(function (result) { |
Can you test multiple numbers, for example [1, 2, 3] at once?
I am looking for the candidate to know how to execute multiple promises in parallel. Q, Bluebird and $q libraries have this feature
1 | Q.all([ |
Does the recursive async version crash for large N?
Yes, the candidate should just try running the same large N found earlier against the promise-returning factorial.
Advanced: rewrite async factorial to avoid stack exhaustion
I am looking for understanding the different between creating a chain of promises and executing a function. A simple solution is to build the chain first without using recursion and then execute it.
1 | var Q = require('q'); |
Notice there is no call to factorial
anywhere inside this code. The only detail is binding the value of n
when creating the then
callback function.
This function can "compute" factorial for n = 100k. I am putting the word compute in quotes because the actual
value quickly reaches Infinity
. But the stack is never exhausted!
Advanced: write async factorial using native ES6 promises
Native promises are already in V8 and the syntax is very simple. To avoid using Q when building promise chain one can write
1 | function valuePromise(n) { |
Advanced: write tail call optimized version of recursive sync function
If the candidate is familiar with the tail call optimization, he / she could write the ES6 version and show the 6to5 transpiler transforming recursive code to the imperative one.
1 | function factorial(n, acc = 1) { |
Notice the accumulator parameter that replaced n * factorial(n - 1)
expression. The recursive call
is by itself at the tail of the function.
Advanced: write factorial using generators
Generators exit the function AND keep the internal state after each yield
. Thus we can keep multiplying
the number without growing the stack.
1 | function* Factorial() { |
How to prepare for these interviews?
- Read a few of my favorite JavaScript books.
- Read my other blog posts.
- Follow me on twitter @bahmutov
- Read about upcoming ES6 features