1. What is Node.js?
Node.js is a runtime environment that allows you to execute JavaScript code on the server side. It is built on the V8 JavaScript runtime and provides an event-driven, non-blocking I/O model.
2. Explain the use of npm.
npm (Node Package Manager) is a package manager for Node.js that allows you to easily install, manage, and share packages of code, making it easy to reuse code across different projects.
3. What is a first class function in Javascript?
When functions can be treated like any other variable then those functions are first-class functions. There are many other programming languages, for example, scala, Haskell, etc which follow this including JS. Now because of this function can be passed as a param to another function(callback) or a function can return another function(higher-order function). map() and filter() are higher-order functions that are popularly used.
4. How do you manage packages in your node.js project?
It can be managed by a number of package installers and their configuration file accordingly. Out of them mostly use npm or yarn. Both provide almost all libraries of javascript with extended features of controlling environment-specific configurations. To maintain versions of libs being installed in a project we use package.json and package-lock.json so that there is no issue in porting that app to a different environment.
5. How is Node.js better than other frameworks most popularly used?
-
Node.js provides simplicity in development because of its non-blocking I/O and event-based model results in short
response time and concurrent processing, unlike other frameworks where developers have to use thread
management.
-
It runs on a chrome v8 engine which is written in c++ and is highly performant with constant improvement.
-
Also since we will use Javascript in both the frontend and backend the development will be much faster.
- And at last, there are sample libraries so that we don’t need to reinvent the wheel.
6. Explain the steps how “Control Flow” controls the functions calls?
- Control the order of execution
- Collect data
- Limit concurrency
- Call the following step in the program.
7. What are some commonly used timing features of Node.js?
- setTimeout/clearTimeout – This is used to implement delays in code execution.
- setInterval/clearInterval – This is used to run a code block multiple times.
- setImmediate/clearImmediate – Any function passed as the setImmediate() argument is a callback that\'s executed in the next iteration of the event loop.
- process.nextTick – Both setImmediate and process.nextTick appear to be doing the same thing; however, you may prefer one over the other depending on your callback’s urgency.
8. What are the advantages of using promises instead of callbacks?
The main advantage of using promise is you get an object to decide the action that needs to be taken after the async task completes. This gives more manageable code and avoids callback hell.
9. What is fork in node JS?
A fork in general is used to spawn child processes. In node it is used to create a new instance of v8 engine to run multiple workers to execute the code.
10. Why is Node.js single-threaded?
Node.js was created explicitly as an experiment in async processing. This was to try a new theory of doing async processing on a single thread over the existing thread-based implementation of scaling via different frameworks.
11. How many types of API functions are there in Node.js?
There are two types of API functions:
-
Asynchronous, non-blocking functions - mostly I/O operations which can be fork out of the main
loop.
- Synchronous, blocking functions - mostly operations that influence the process running in the main loop.
12. What is REPL?
PL in Node.js stands for Read, Eval, Print, and Loop, which further means evaluating code on the go.
13. List down the two arguments that async.queue takes as input?
- Task Function
- Concurrency Value
14. What is the purpose of module.exports?
This is used to expose functions of a particular module or file to be used elsewhere in the project. This can be used
to encapsulate all similar functions in a file which further improves the project structure.
For example,
you have a file for all utils functions with util to get solutions in a different programming language of a problem
statement.
const getSolutionInJavaScript = async ({ problem_id}) => {...};const getSolutionInPython = async ({ problem_id}) => {...};module.exports = { getSolutionInJavaScript, getSolutionInPython }
Thus using module.exports we can use these functions in some other file:
const { getSolutionInJavaScript, getSolutionInPython} = require("./utils")
15. What tools can be used to assure consistent code style?
ESLint can be used with any IDE to ensure a consistent coding style which further helps in maintaining the codebase.
16. What do you understand by callback hell?
async_A(function(){ async_B(function(){ async_C(function(){ async_D(function(){ .... }); }); });});
For the above example, we are passing callback functions and it makes the code unreadable and not maintainable, thus we should change the async logic to avoid this.
17. If Node.js is single threaded then how does it handle concurrency?
The main loop is single-threaded and all async calls are managed by libuv library.
For example:
const crypto = require("crypto");const start = Date.now();function logHashTime() { crypto.pbkdf2("a", "b", 100000, 512, "sha512", () => {console.log("Hash: ", Date.now() - start); });}logHashTime();logHashTime();logHashTime();logHashTime();
This gives the output:
Hash: 1213Hash: 1225Hash: 1212Hash: 1222
This is because libuv sets up a thread pool to handle such concurrency. How many threads will be there in the thread pool depends upon the number of cores but you can override this.
18. Differentiate between process.nextTick() and setImmediate()?
Both can be used to switch to an asynchronous mode of operation by listener functions.
process.nextTick()
sets the callback to execute but setImmediate pushes the callback in the queue to be executed. So the event loop runs
in the following manner
timers–>pending callbacks–>idle,prepare–>connections(poll,data,etc)–>check–>close callbacks
In this process.nextTick() method adds the callback function to the start of the next event queue and setImmediate() method to place the function in the check phase of the next event queue.
19. How does Node.js overcome the problem of blocking of I/O operations?
Since the node has an event loop that can be used to handle all the I/O operations in an asynchronous manner without
blocking the main function.
So for example, if some network call needs to happen it will be scheduled
in the event loop instead of the main thread(single thread). And if there are multiple such I/O calls each one will be
queued accordingly to be executed separately(other than the main thread).
Thus even though we have single-threaded JS, I/O ops are handled in a nonblocking way.
20. How can we use async await in node.js?
Here is an example of using async-await pattern:
// this code is to retry with exponential backofffunction wait (timeout) { return new Promise((resolve) => {setTimeout(() => { resolve()}, timeout); });}async function requestWithRetry (url) { const MAX_RETRIES = 10; for (let i = 0; i <= MAX_RETRIES; i++) {try { return await request(url);} catch (err) { const timeout = Math.pow(2, i); console.log(\'Waiting\', timeout, \'ms\'); await wait(timeout); console.log(\'Retrying\', err.message, i);} }}
21. What is node.js streams?
Streams are instances of EventEmitter which can be used to work with streaming data in Node.js. They can be used for
handling and manipulating streaming large files(videos, mp3, etc) over the network. They use buffers as their
temporary storage.
There are mainly four types of the stream:
- Writable: streams to which data can be written (for example, fs.createWriteStream()).
- Readable: streams from which data can be read (for example, fs.createReadStream()).
- Duplex: streams that are both Readable and Writable (for example, net.Socket).
- Transform: Duplex streams that can modify or transform the data as it is written and read (for example, zlib.createDeflate()).
22. What are node.js buffers?
In general, buffers is a temporary memory that is mainly used by stream to hold on to some data until consumed. Buffers are introduced with additional use cases than JavaScript’s Unit8Array and are mainly used to represent a fixed-length sequence of bytes. This also supports legacy encodings like ASCII, utf-8, etc. It is a fixed(non-resizable) allocated memory outside the v8.
23. What is middleware?
Middleware comes in between your request and business logic. It is mainly used to capture logs and enable rate limit, routing, authentication, basically whatever that is not a part of business logic. There are third-party middleware also such as body-parser and you can write your own middleware for a specific use case.
24. Explain what a Reactor Pattern in Node.js?
Reactor pattern again a pattern for nonblocking I/O operations. But in general, this is used in any event-driven
architecture.
There are two components in this: 1. Reactor 2. Handler.
Reactor: Its job is to dispatch the I/O event to appropriate handlers
Handler: Its job is to actually
work on those events
25. Why should you separate Express app and server?
The server is responsible for initializing the routes, middleware, and other application logic whereas the app has all the business logic which will be served by the routes initiated by the server. This ensures that the business logic is encapsulated and decoupled from the application logic which makes the project more readable and maintainable.
26. Describe the exit codes of Node.js?
Exit codes give us an idea of how a process got terminated/the reason behind termination.
A few of
them are:
- Uncaught fatal exception - (code - 1) - There has been an exception that is not handled
- Unused - (code - 2) - This is reserved by bash
- Fatal Error - (code - 5) - There has been an error in V8 with stderr output of the description
- Internal Exception handler Run-time failure - (code - 7) - There has been an exception when bootstrapping function was called
- Internal JavaScript Evaluation Failure - (code - 4) - There has been an exception when the bootstrapping process failed to return function value when evaluated.
27. What is an Event Emitter in Node.js?
EventEmitter is a Node.js class that includes all the objects that are basically capable of emitting events. This can be done by attaching named events that are emitted by the object using an eventEmitter.on() function. Thus whenever this object throws an even the attached functions are invoked synchronously.
const EventEmitter = require(\'events\');class MyEmitter extends EventEmitter {}const myEmitter = new MyEmitter();myEmitter.on(\'event\', () => { console.log(\'an event occurred!\');});myEmitter.emit(\'event\');
28. How are worker threads different from clusters?
Cluster:
- There is one process on each CPU with an IPC to communicate.
- In case we want to have multiple servers accepting HTTP requests via a single port, clusters can be helpful.
- The processes are spawned in each CPU thus will have separate memory and node instance which further will lead to memory issues.
Worker threads:
- There is only one process in total with multiple threads.
- Each thread has one Node instance (one event loop, one JS engine) with most of the APIs accessible.
- Shares memory with other threads (e.g. SharedArrayBuffer)
- This can be used for CPU-intensive tasks like processing data or accessing the file system since NodeJS is single-threaded, synchronous tasks can be made more efficient leveraging the worker\'s threads.
29. How to measure the duration of async operations?
Performance API provides us with tools to figure out the necessary performance metrics. A simple example would be using async_hooks and perf_hooks
\'use strict\';const async_hooks = require(\'async_hooks\');const { performance, PerformanceObserver} = require(\'perf_hooks\');const set = new Set();const hook = async_hooks.createHook({ init(id, type) {if (type === \'Timeout\') { performance.mark(`Timeout-${id}-Init`); set.add(id);} }, destroy(id) {if (set.has(id)) { set.delete(id); performance.mark(`Timeout-${id}-Destroy`); performance.measure(`Timeout-${id}`, `Timeout-${id}-Init`, `Timeout-${id}-Destroy`);} }});hook.enable();const obs = new PerformanceObserver((list, observer) => { console.log(list.getEntries()[0]); performance.clearMarks(); observer.disconnect();});obs.observe({ entryTypes: [\'measure\'], buffered: true });setTimeout(() => {}, 1000);
This would give us the exact time it took to execute the callback.