A few questions for quickly assessing candidate's Node.js skill level
- Write a function that simulates an asynchronous I/O function call. The function should be called
ping
. It should accept one argument calleddelay
that will determine how many seconds before the function will call back with a response (pong
). If no argument is provided, it should call back immediately. If the argument is greater than 3, it should call back with an error.
function ping(delay, callback) {
// if delay is not provided, it should default to 0
if (typeof delay === 'function') {
callback = delay;
delay = 0;
}
try {
delay = parseInt(delay, 10);
if (delay > 3) {
setImmediate(function() {
// MUST call back asynchronously
callback(new Error('delay value > 3: ' + delay));
})
} else {
setTimeout(function() {
callback(null, 'pong');
}, delay * 1000);
}
} catch (err) {
// MUST call back asynchronously
setImmediate(function() {
callback(err);
});
}
}
- Print an array of numbers to the console without using a loop
[ 1, 2, 3 ].forEach(function(n) {
console.log(n);
});
- Use the functional approach to converting an array of numbers to
an array of the squares of the numbers (
[1,2,3] -> [1,4,9]
)
var squares = [ 1, 2, 3 ].map(function(n) {
return n * n;
});
- Using the
ping
async function we created earlier, take an array ofdelay
values and invoke theping
function using theasync
library and print each result.
Note: it's ok if candidate is not familiar with async
library if the
candidate at least could either implement something reasonable for
chaining callbacks, or has already been using promises or async/await
and can come up with something reasonable based on that. The candidate
would have to understand how to promisify the ping
function, or
at the very least be familiar with promisifying it using a library
like bluebird.
async.each([1, 2, 3], ping, function(result, callback) {
console.log(result);
callback();
}, function(err) {
if (err) {
console.log(err);
} else {
console.log('done');
}
});
For example, with async/await...
let pingAsync = bluebird.promisify(ping)
[1, 2, 3].forEach(n => {
try {
let result = await pingAsync(n)
console.log(`${n} -> ${result}`)
} catch (err) {
console.log(`error: ${n} -> ${err.message}`)
}
})
- Discuss scope in JavaScript
Candidate should discuss function scope
Candidate should understand hoisting.
Extra points if candidate can discuss ES6 block scope, and let
and const
keyword (as opposed to var
).
- Discuss
apply
,call
,bind
, andthis
context.
- use
apply
on a function that expects a number of arguments when you have an array:func.apply(thisArg, [arrayLikeObject])
Candidate should be able to note that the
arrayLikeObject
simply means any object that has alength
property (such asarguments
automatic local variable).
-
call
is likeapply
, except you provide an argument list instead of a single array-like object:func.call(thisArg[, arg1, ...])
-
bind
is used on a function to return a new function that wraps it, using thethis
value that was provide to set the function'sthis
context:var f = func.bind(thisArg[, arg1, ...])
Note that the optional arguments will be prepended to the argument list when actually invoking the newly returned function.
Candidate should be able to explain how using
bind
is an alternative to storing thethis
context before invoking a function that will callback, where in the callback you need to have access to the correct context. For example, many developers will writevar that = this
so that they can usethat
in a callback. Withbind
, it is unnecessary to savethis
in another variable.
- Describe the Node.js concurrency model
Looking to hear candidate mention things about:
asynchronous non-blocking i/o event-driven / event loop callbacks
single threaded model from programmer perspective; async work happens in callbacks, which run until completion (which might possibly include making other requests that will have callbacks run at some point in the future)
Extra points if candidate contrasts against and explains disadvantages of thread-based concurrency model:
threaded code execution paths can be difficult to reason through
synchronized access to shared state is error prone, can be difficult to debug issues, errors can be non-deterministic (race conditions)
threads are relatively inefficent compared to callbacks (stack allocation, context switches)