As an asynchronous event-driven JavaScript runtime, Node.js is designed to build scalable network applications. Follow along to refresh your knowledge and explore the 52 most frequently asked and advanced Node JS Interview Questions and Answers every JavaScript developer must be ready before the next Nodejs interview.
npm packages installationThe main difference between local and global packages is this:
npm install <package-name>, and they are put in the node_modules folder under this directorynpm install -g <package-name>In general, all packages should be installed locally.
Following are main benefits of using Node.js
Let’s look at some of the key features of Node.js.
Asynchronous JavaScript, or JavaScript that uses callbacks, is hard to get right intuitively. A lot of code ends up looking like this:
fs.readdir(source, function (err, files) {
if (err) {
console.log('Error finding files: ' + err)
} else {
files.forEach(function (filename, fileIndex) {
console.log(filename)
gm(source + filename).size(function (err, values) {
if (err) {
console.log('Error identifying file size: ' + err)
} else {
console.log(filename + ' : ' + values)
aspect = (values.width / values.height)
widths.forEach(function (width, widthIndex) {
height = Math.round(width / aspect)
console.log('resizing ' + filename + 'to ' + height + 'x' + height)
this.resize(width, height).write(dest + 'w' + width + '_' + filename, function(err) {
if (err) console.log('Error writing file: ' + err)
})
}.bind(this))
}
})
})
}
})See the pyramid shape and all the }) at the end? This is affectionately known as callback hell.
The cause of callback hell is when people try to write JavaScript in a way where execution happens visually from top to bottom. Lots of people make this mistake! In other languages like C, Ruby or Python there is the expectation that whatever happens on line 1 will finish before the code on line 2 starts running and so on down the file.
A callback is a function called at the completion of a given task; this prevents any blocking, and allows other code to be run in the meantime. Callbacks are the foundation of Node.js. Callbacks give you an interface with which to say, "and when you're done doing that, do all this."
var myCallback = function(data) {
console.log('got data: '+data);
};
var usingItNow = function(callback) {
callback('get it?');
};The V8 library provides Node.js with a JavaScript engine (a program that converts Javascript code into lower level or machine code that microprocessors can understand), which Node.js controls via the V8 C++ API. V8 is maintained by Google, for use in Chrome.
The Chrome V8 engine :
libuv?libuv is a C library that is used to abstract non-blocking I/O operations to a consistent interface across all supported platforms. It provides mechanisms to handle file system, DNS, network, child processes, pipes, signal handling, polling and streaming. It also includes a thread pool for offloading work for some things that can't be done asynchronously at the operating system level.
return callback();
//some more lines of code; - won't be executed
callback();
//some more lines of code; - will be executedOf course returning will help the context calling async function get the value returned by callback.
function do2(callback) {
log.trace('Execute function: do2');
return callback('do2 callback param');
}
var do2Result = do2((param) => {
log.trace(`print ${param}`);
return `return from callback(${param})`; // we could use that return
});
log.trace(`print ${do2Result}`);Output:
C:\Work\Node>node --use-strict main.js
[0] Execute function: do2
[0] print do2 callback param
[0] print return from callback(do2 callback param)require modules at the top of a file? Can we require modules inside of functions?Yes, we can but we shall never do it.
Node.js always runs require synchronously. If you require an external module from within functions your module will be synchronously loaded when those functions run and this can cause two problems:
The modules used in Node.js follow a module specification known as the CommonJS specification. The recent updates to the JavaScript programming language, in the form of ES6, specify changes to the language, adding things like new class syntax and a module system. This module system is different from Node.js modules. To import ES6 module, we'd use the ES6 import functionality.
Now ES6 modules are incompatible with Node.js modules. This has to do with the way modules are loaded differently between the two formats. If you use a compiler like Babel, you can mix and match module formats.
Consider this code:
import { EventEmitter } from 'events';
const eventEmitter = new EventEmitter();
eventEmitter.on('myEvent', (data) => {
console.log(data, '- FIRST');
});
console.log('Statement A');
eventEmitter.on("myEvent", data => {
console.log(data, '- SECOND');
});
eventEmitter.emit('myEvent', 'Emitted Statement');
console.log("Statement B");What will be the output of this code and why?
When executed, the above code gives the output:
> Statement A
> Emitted Statement - FIRST
> Emitted Statement - SECOND
> Statement BThe listeners are executed in the order the listeners are created for an event emitter.
async/await use in the forEach loopConsider this code:
import fs from 'fs-promise'
async function printFiles () {
const files = await getFilePaths() // Assume this works fine
files.forEach(async (file) => {
const contents = await fs.readFile(file, 'utf8')
console.log(contents)
})
}
printFiles()Is it working as expected? Can you fix it?
The code doesn't do what you expect it to do. It just fires off multiple asynchronous calls, but the printFiles function does immediately return after that.
If you want to read the files in sequence, you cannot use forEach indeed. Just use a modern for … of loop instead, in which await will work as expected:
async function printFiles () {
const files = await getFilePaths();
for (const file of files) {
const contents = await fs.readFile(file, 'utf8');
console.log(contents);
}
}I have this code:
for (var i = 0; i < 5; i++) {
setTimeout(function () {
console.log(i);
}, i);
}But the output is unexpected:
5
5
5
5
5Can you fix it to be?
0
1
2
3
4The reason this happens is because each timeout is created and then i is incremented. Then when the callback is called, it looks for the value of i and it is 5. The solution is to create a closure so that the current value of i is stored. For example:
for (var i = 0; i < 5; i++) {
(function(i) {
setTimeout(function () {
console.log(i);
}, i);
})(i);
}Node.js, in its essence, is a single thread process. It does not expose child threads and thread management methods to the developer. js does spawn child threads for certain tasks such as asynchronous I/O, but these run behind the scenes and do not execute any application JavaScript code, nor block the main event loop.
If threading support is desired in a Node.js application, there are tools available to enable it, such as the ChildProcess module.
Node.js internally uses a single-threaded event loop to process queued events. But this approach may lead to blocking the entire process if there is a task running longer than expected. Node.js addresses this problem by incorporating callbacks also known as higher-order functions. So whenever a long-running process finishes its execution, it triggers the callback associated. Sometimes, it could lead to complex and unreadable code. More the no. of callbacks, longer the chain of returning callbacks would be.
There are four solutions which can address the callback hell problem:
Make your program modular - It proposes to split the logic into smaller modules. And then join them together from the main module to achieve the desired result.
Use async/await mechanism - Async /await is another alternative for consuming promises, and it was implemented in ES8, or ES2017. Async/await is a new way of writing promises that are based on asynchronous code but make asynchronous code look and behave more like synchronous code.
Use promises mechanism - Promises give an alternate way to write async code. They either return the result of execution or the error/exception. Implementing promises requires the use of .then() function which waits for the promise object to return. It takes two optional arguments, both functions. Depending on the state of the promise only one of them will get called. The first function call proceeds if the promise gets fulfilled. However, if the promise gets rejected, then the second function will get called.
Use generators - Generators are lightweight routines, they make a function wait and resume via the yield keyword. Generator functions uses a special syntax function* (). They can also suspend and resume asynchronous operations using constructs such as promises or thunks and turn a synchronous code into asynchronous.
function* HelloGen() {
yield 100;
yield 400;
}
var gen = HelloGen();
console.log(gen.next()); // {value: 100, done: false}
console.log(gen.next()); // {value: 400, done: false}
console.log(gen.next()); // {value: undefined, done: true}If you want to read the files in sequence, just use a modern for … of loop, in which await will work as expected:
async function printFiles () {
const files = await getFilePaths();
for (const file of files) {
const contents = await fs.readFile(file, 'utf8');
console.log(contents);
}
}Events are synchronous and blocking. The events raised by event emitters are synchronously executed by the listeners in the current event loop’s iteration. They are implemented with simple function calls. If you look at the eventEmitter code, to send an event to all listeners, it literally just iterates through an array of listeners and calls each listener callback, one after the other.
Consider:
import { EventEmitter } from 'events';
const eventEmitter = new EventEmitter();
eventEmitter.on('myEvent', (data) => {
console.log(data);
});
console.log('Statement A');
eventEmitter.emit('myEvent', 'Statement B');
console.log("Statement C");When we execute this code snippet, we get the following output in the console:
> Statement A
> Statement B
> Statement CReasons to use NodeJS:
async/awaitRewrite this code to async/await:
function asyncTask() {
return functionA()
.then((valueA) => functionB(valueA))
.then((valueB) => functionC(valueB))
.then((valueC) => functionD(valueC))
.catch((err) => logger.error(err))
}async function asyncTask() {
try {
const valueA = await functionA()
const valueB = await functionB(valueA)
const valueC = await functionC(valueB)
return await functionD(valueC)
} catch (err) {
logger.error(err)
}
}Pure JavaScript, while great with unicode-encoded strings, does not handle straight binary data very well. This is fine on the browser, where most data is in the form of strings. However, Node.js servers have to also deal with TCP streams and reading and writing to the filesystem, both of which make it necessary to deal with purely binary streams of data.
The Buffer class in Node.js is designed to handle raw binary data. Each buffer corresponds to some raw memory allocated outside V8. Buffers act somewhat like arrays of integers, but aren't resizable and have a whole bunch of methods specifically for binary data.
var buffer = Buffer.alloc(16)
buffer.write("Hello", "utf-8")
buffer.write(" world!", 5, "utf-8")
buffer.toString('utf-8')EventEmitter is a class that helps us create a publisher-subscriber pattern in NodeJS.
With an event emitter, we can simply raise a new event from a different part of an application, and a listener will listen to the raised event and have some action performed for the event.
import { EventEmitter } from 'events';
const eventEmitter = new EventEmitter();
// listen to the event
eventEmitter.on('myEvent', () => {
console.log('Data Received'); // this function is the event listener
});
// publish an event
eventEmitter.emit('myEvent');module.exports do in Node.js, and what would a simple example be?module.exports is the object that's actually returned as the result of a require call.
The exports variable is initially set to that same object (i.e. it's a shorthand "alias"), so in the module code you would usually write something like this:
let myFunc1 = function() { ... };
let myFunc2 = function() { ... };
exports.myFunc1 = myFunc1;
exports.myFunc2 = myFunc2;to export (or "expose") the internally scoped functions myFunc1 and myFunc2.
And in the calling code you would use:
const m = require('./mymodule');
m.myFunc1();where the last line shows how the result of require is (usually) just a plain object whose properties may be accessed.
Chanining is a mechanism to connect output of one stream to another stream and create a chain of multiple stream operations. It is normally used with piping operations. if we’re piping into a duplex stream, we can chain pipe calls just like we do in Linux:
readableSrc
.pipe(transformStream1)
.pipe(transformStream2)
.pipe(finalWrtitableDest)The pipe method returns the destination stream, which enabled us to do the chaining above. For streams a (readable), b and c (duplex), and d (writable), we can:
a.pipe(b).pipe(c).pipe(d)
# Which is equivalent to:
a.pipe(b)
b.pipe(c)
c.pipe(d)
# Which, in Linux, is equivalent to:
$ a | b | c | dstream and what are types of streams available in Node.js?A stream is an abstract interface for working with streaming data in Node.js.
Streams basically provide two major advantages over using other data handling methods:
There are 4 types of streams in Node.js:
fs.createWriteStream() lets us write data to a file using streams.fs.createReadStream() lets us read the contents of a file.net.SocketA blocking call causes results to be returned synchronously.
Performing a blocking system call causes the process to enter the blocked state. Control is let back to the process only after the I/O event that is being waited upon occurs.
const fs = require("fs");
const contents = fs.readFileSync("file.txt", "utf8");
// this line is not reached until the read results are in
console.log(contents);fs module?Every method in fs module has synchronous as well as asynchronous form.
cluster and worker_threads packages in Node.js?Effectively what you are differing is process based vs thread based. Threads share memory (e.g. SharedArrayBuffer) whereas processes don't.
SharedArrayBuffer)require(x) and ES6 import x in Node.js?import (ES6) is the future of the Javascript language in both Node.js and the browser and is used in ECMAScript modules (ESM modules) for loading other modules, either statically or dynamically.
ES6 → import, export default, export
// hello.js
function hello() {
return 'hello'
}
export default hello
// app.js
import hello from './hello'
hello() // returns hellorequire() is the original way that Node.js loaded modules and is used in CommonJS modules. require() is natively supported in Node.js, but not in browsers (though there are some 3rd party libraries that have require-like module loaders for the browser).
CommonJS → require, module.exports, exports.foo
// hello.js
function hello1() {
return 'hello1'
}
function hello2() {
return 'hello2'
}
module.exports = {
hello1,
hello2
}
// app.js
const hello = require('./hello')
hello.hello1() // returns hello1
hello.hello2() // returns hello2Unhandled exceptions in Node.js can be caught at the Process level by attaching a handler for uncaughtException event.
process.on('uncaughtException', function(err) {
console.log('Caught exception: ' + err);
});However, uncaughtException is a very crude mechanism for exception handling and may be removed from Node.js in the future. An exception that has bubbled all the way up to the Process level means that your application, and Node.js may be in an undefined state, and the only sensible approach would be to restart everything.
The preferred way is to add another layer between your application and the Node.js process which is called the domain.
Domains provide a way to handle multiple different I/O operations as a single group. So, by having your application, or part of it, running in a separate domain, you can safely handle exceptions at the domain level, before they reach the Process level.
The event loop is what allows Node.js to perform non-blocking I/O operations — despite the fact that JavaScript is single-threaded — by offloading operations to the system kernel whenever possible.
Every I/O requires a callback - once they are done they are pushed onto the event loop for execution. Since most modern kernels are multi-threaded, they can handle multiple operations executing in the background. When one of these operations completes, the kernel tells Node.js so that the appropriate callback may be added to the poll queue to eventually be executed.
module.exports vs a class vs an object literal when defining Node.js modules?Consider:
Module exports:
//myModule.js
module.exports = {
function func1() {
console.log("func1");
}
function func2() {
console.log("func2");
}
}
//in another js file
const myModule = require('myModule');
myModule.func1();
myModule.func2();The class would look something like this. We could also do this the javascript prototype way, but doing this here for simplicity.
//myClass.js
class myClass {
func1() {
console.log("Func1");
}
func2() {
console.log("Func2");
}
}
module.exports = myClass;
//in another js file
const myClass = require('myClass');
let newClass = new myClass();
newClass.func1();
newClass.func2();Finally, we could have an object literal singleton.
//myObj.js
let myObj = {
func1: function() {
console.log("Func1");
},
func2: function() {
console.log("Func2");
}
};
module.exports = myObj;
//in another js file
let myObj = require('myObj');
myObj.func1();
myObj.func2();When is it best to use a module or a class or an object literal?
A class should be used when there's a chance of it being used properly as a class (for example, if you ever want to put or retrieve data on an instance, where that instance also has methods, a class makes perfect sense).
Consider:
// MyClass.js
class MyClass {
func1() {
console.log("Func1");
}
func2() {
console.log("Func2");
}
setData(data) {
this.data = data;
}
getData() {
return this.data;
}
}
const instance = new MyClass();
instance.setData('foo');
console.log(instance.getData());But if, as this case appears to be, you only want to collect similar functions together, a class makes the code much more confusing for no real benefit. Readers of the code (which may include yourself, somewhere down the line) will expect a class to be used for something that a plain object can't do.
Your myModule.js is pretty much identical to your myObj.js - both are exporting a plain object which has certain properties on it. The only difference is the existence of the intermediate variable myObj, which isn't used aside from being an intermediate variable.
Defining the object to be exported in a standalone variable before exporting it can help a bit when you want to reliably reference other properties of the exported object:
// a little bit verbose, have to reference `module.exports` every time
// to get to the exported object:
module.exports = {
getSummary() {
// using "this" instead may not be reliable without a `bind`
const { summary } = module.exports.getAllInfo();
return summary;
}
getAllInfo() {
// ...
}
};// a little bit more DRY:
const obj = {
getSummary() {
const { summary } = obj.getAllInfo();
return summary;
}
getAllInfo() {
// ...
}
};
module.exports = obj;You also might (or might not) consider defining the object to be exported in a standalone variable to be more readable. You can use whatever you like that suites your use-case.
Node.js is well suited for applications that have
because the event loop (with all the other clients) is blocked during the execution of a function. Node.js is best suited for real-time applications:
or anything, where what one user does with the application, needs to be seen by other users immediately, without a page refresh.
In addition, Node.js is especially suited for applications where you'd like to maintain a persistent connection from the browser back to the server. Using a technique known as "long-polling", you can write an application that sends updates to the user in real time. Doing long polling on many of the web's giants, like Ruby on Rails or Django, would create immense load on the server, because each active client eats up one server process. This situation amounts to a tarpit attack. When you use something like Node.js, the server has no need of maintaining separate threads for each open connection.
cluster module in Node.js?The Node.js cluster module is used with Node.js any time you want to spread out the request processing across multiple node.js processes. This is most often used when you wish to increase your ability to handle more requests/second and you have multiple CPU cores in your server. By default, a single instance of Node.js will not fully utilise multiple cores because the core Javascript you run in your server is single-threaded (uses one core).
Clustering also provides you with some additional fault tolerance. If one cluster process goes down, you can still have other live clusters serving requests while the disabled cluster restarts.
Because each cluster is a separate process, there is no automatic shared data among the different cluster processes. As such, clustering is simplest to implement either where there is no shared data or where the shared data is already in a place where it can be accessed by multiple processes (such as in a database).
const http = require('http');
const cluster = require('cluster');
const os = require('os');
const process = require('process');
const express = require('express');
const port = 8000;
const numCPUs = os.cpus().length;
console.log(`No of cpu = ${numCPUs}`);
//Creating child processes from main process based on number of cpus
if (cluster.isMaster) {
console.log(`Primary ${process.pid} is running`);
// Fork workers.
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
console.log(`worker ${worker.process.pid} died`);
cluster.fork();
});
} else {
const app = express();
app.get('/', (req, res) => {
console.log(`Incoming req accepted by Process ${process.pid}`);
for(let i=0; i<9999999999999999999; i++) {
}
res.send('hello world');
});
app.get('/test', (req, res) => {
console.log(`Incoming req accepted by Process ${process.pid}`);
res.send('Quickly say Hello World');
});
app.listen(port, () => {
console.log(`app is listening at port ${port} by Process ${process.pid}`);
});
}assert library vs. other assert libraries like chai? Why?Basically, you can achieve all the things with Node's very own assert module that you can do with all the other modules out there. Sticking to node.js assert limits your dependency list. There is no technical reason why to prefer one over the other, but readability and null compatibility may be reasons. The only reason why chai's assert exist is so if you read the code you can get a better understanding of the tests, but that's about it.
Compare:
For example, testing for a null value with Node.js:
assert(foo === null);
And using chai:
assert.isNull(foo);
module.exports vs exports in that Node.js code sampleArrange-Act-Assert pattern?async code in Node.js?Class in Node.js?dev and prod environmentsspawn and execute functions in Node.js? When to use each one?process.nextTick() and setImmediate()?Rust has been Stack Overflow’s most loved language for four years in a row and emerged as a compelling language choice for both backend and system developers, offering a unique combination of memory safety, performance, concurrency without Data races...
Clean Architecture provides a clear and modular structure for building software systems, separating business rules from implementation details. It promotes maintainability by allowing for easier updates and changes to specific components without affe...
Azure Service Bus is a crucial component for Azure cloud developers as it provides reliable and scalable messaging capabilities. It enables decoupled communication between different components of a distributed system, promoting flexibility and resili...