Javascript in advanced

JavaScript, ES6, Node.js & Async/Await Complete Guide
Table of Contents
- JavaScript Fundamentals
- JavaScript Advanced Concepts
- ES6+ Features
- Node.js
- Async/Await & Promises
- Interview Questions
JavaScript Fundamentals
Data Types
JavaScript has 8 data types:
Primitive Types (7):
// 1. String
const name = "John";
// 2. Number
const age = 30;
const price = 19.99;
// 3. BigInt
const bigNumber = 9007199254740991n;
// 4. Boolean
const isActive = true;
// 5. Undefined
let x; // undefined
// 6. Null
const empty = null;
// 7. Symbol
const id = Symbol('id');
Non-Primitive Type (1):
// 8. Object (includes arrays, functions, dates, etc.)
const person = { name: "John", age: 30 };
const numbers = [1, 2, 3];
const greet = function() { return "Hello"; };
Type Coercion
// Implicit coercion
console.log("5" + 3); // "53" (string)
console.log("5" - 3); // 2 (number)
console.log("5" * "2"); // 10 (number)
console.log(true + 1); // 2
console.log(false + 1); // 1
// Explicit coercion
console.log(Number("5")); // 5
console.log(String(123)); // "123"
console.log(Boolean(0)); // false
console.log(Boolean("")); // false
console.log(Boolean(null)); // false
Truthy & Falsy Values
// Falsy values (6 total)
false, 0, "", null, undefined, NaN
// Everything else is truthy
"0", "false", [], {}, function() {}
Comparison Operators
// == (loose equality - performs type coercion)
console.log(5 == "5"); // true
console.log(null == undefined); // true
// === (strict equality - no type coercion)
console.log(5 === "5"); // false
console.log(null === undefined); // false
// Object comparison (by reference)
const a = { x: 1 };
const b = { x: 1 };
const c = a;
console.log(a === b); // false (different references)
console.log(a === c); // true (same reference)
Variable Declarations
// var - function scoped, hoisted
var x = 1;
// let - block scoped, not hoisted (TDZ)
let y = 2;
// const - block scoped, not hoisted, cannot be reassigned
const z = 3;
// Hoisting example
console.log(a); // undefined (var is hoisted)
console.log(b); // ReferenceError (TDZ)
var a = 1;
let b = 2;
Functions
// Function Declaration (hoisted)
function greet(name) {
return `Hello, ${name}!`;
}
// Function Expression (not hoisted)
const greet = function(name) {
return `Hello, ${name}!`;
};
// Arrow Function
const greet = (name) => `Hello, ${name}!`;
// IIFE (Immediately Invoked Function Expression)
(function() {
console.log("Executed immediately!");
})();
// Default Parameters
function greet(name = "World") {
return `Hello, ${name}!`;
}
// Rest Parameters
function sum(...numbers) {
return numbers.reduce((a, b) => a + b, 0);
}
Scope
// Global Scope
var globalVar = "I'm global";
function outer() {
// Function Scope
var functionVar = "I'm in function";
if (true) {
// Block Scope (let, const)
let blockVar = "I'm in block";
var notBlockVar = "I'm still function scoped";
}
console.log(notBlockVar); // Works
console.log(blockVar); // ReferenceError
}
Arrays
const arr = [1, 2, 3, 4, 5];
// Mutating methods
arr.push(6); // Add to end
arr.pop(); // Remove from end
arr.shift(); // Remove from start
arr.unshift(0); // Add to start
arr.splice(1, 2); // Remove/replace elements
arr.sort(); // Sort in place
arr.reverse(); // Reverse in place
// Non-mutating methods
arr.map(x => x * 2); // Transform elements
arr.filter(x => x > 2); // Filter elements
arr.reduce((a, b) => a + b); // Reduce to single value
arr.find(x => x > 2); // Find first match
arr.findIndex(x => x > 2); // Find index of first match
arr.some(x => x > 2); // Any element matches?
arr.every(x => x > 0); // All elements match?
arr.includes(3); // Contains element?
arr.slice(1, 3); // Extract portion
arr.concat([6, 7]); // Merge arrays
arr.flat(); // Flatten nested arrays
arr.flatMap(x => [x, x * 2]); // Map + flatten
Objects
const person = {
name: "John",
age: 30,
greet() {
return `Hello, I'm ${this.name}`;
}
};
// Accessing properties
person.name; // Dot notation
person["name"]; // Bracket notation
// Object methods
Object.keys(person); // ["name", "age", "greet"]
Object.values(person); // ["John", 30, function]
Object.entries(person); // [["name", "John"], ["age", 30], ...]
Object.assign({}, person); // Shallow copy
Object.freeze(person); // Immutable (shallow)
Object.seal(person); // Can modify, can't add/delete
// Property descriptors
Object.defineProperty(person, 'id', {
value: 1,
writable: false,
enumerable: true,
configurable: false
});
JavaScript Advanced Concepts
Closures
A closure is a function that has access to variables from its outer (enclosing) scope, even after the outer function has returned.
function createCounter() {
let count = 0; // Private variable
return {
increment() { return ++count; },
decrement() { return --count; },
getCount() { return count; }
};
}
const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.getCount()); // 2
// count is not accessible directly
// Common pitfall with closures in loops
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100); // 3, 3, 3
}
// Fix with let (block scope)
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100); // 0, 1, 2
}
// Fix with closure
for (var i = 0; i < 3; i++) {
((j) => {
setTimeout(() => console.log(j), 100);
})(i); // 0, 1, 2
}
this Keyword
// 1. Global context
console.log(this); // Window (browser) or global (Node.js)
// 2. Object method
const obj = {
name: "Object",
greet() {
console.log(this.name); // "Object"
}
};
// 3. Arrow functions (lexical this)
const obj2 = {
name: "Object",
greet: () => {
console.log(this.name); // undefined (inherits from enclosing scope)
},
greetCorrect() {
const inner = () => {
console.log(this.name); // "Object" (inherits from greetCorrect)
};
inner();
}
};
// 4. Explicit binding
function greet() {
console.log(this.name);
}
const person = { name: "John" };
greet.call(person); // "John" - immediate execution
greet.apply(person); // "John" - immediate execution
const boundGreet = greet.bind(person);
boundGreet(); // "John" - returns new function
// call vs apply
function introduce(greeting, punctuation) {
console.log(`${greeting}, I'm ${this.name}${punctuation}`);
}
introduce.call(person, "Hello", "!"); // Arguments as list
introduce.apply(person, ["Hello", "!"]); // Arguments as array
// 5. Constructor (new keyword)
function Person(name) {
this.name = name;
}
const john = new Person("John");
console.log(john.name); // "John"
Prototypes & Inheritance
// Prototype chain
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function() {
console.log(`${this.name} makes a sound`);
};
function Dog(name, breed) {
Animal.call(this, name);
this.breed = breed;
}
// Set up inheritance
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.speak = function() {
console.log(`${this.name} barks`);
};
const dog = new Dog("Rex", "German Shepherd");
dog.speak(); // "Rex barks"
// Prototype lookup
console.log(dog.__proto__ === Dog.prototype); // true
console.log(Dog.prototype.__proto__ === Animal.prototype); // true
console.log(Animal.prototype.__proto__ === Object.prototype); // true
// Check prototype
console.log(dog instanceof Dog); // true
console.log(dog instanceof Animal); // true
console.log(dog.hasOwnProperty('name')); // true
console.log(dog.hasOwnProperty('speak')); // false (on prototype)
Event Loop & Call Stack
console.log('1'); // Synchronous - Call Stack
setTimeout(() => {
console.log('2'); // Macro task - Task Queue
}, 0);
Promise.resolve().then(() => {
console.log('3'); // Micro task - Microtask Queue
});
console.log('4'); // Synchronous - Call Stack
// Output: 1, 4, 3, 2
// Execution order:
// 1. Synchronous code runs first (Call Stack)
// 2. Microtasks (Promises, queueMicrotask, MutationObserver)
// 3. Macrotasks (setTimeout, setInterval, setImmediate, I/O)
┌───────────────────────────┐
│ Call Stack │
│ (Synchronous code) │
└───────────────────────────┘
↓
┌───────────────────────────┐
│ Microtask Queue │
│ (Promises, async/await) │
└───────────────────────────┘
↓
┌───────────────────────────┐
│ Macrotask Queue │
│ (setTimeout, I/O, etc.) │
└───────────────────────────┘
Memory Management & Garbage Collection
// Memory leaks examples
// 1. Forgotten timers
const data = getData();
setInterval(() => {
console.log(data); // data is never garbage collected
}, 1000);
// 2. Detached DOM nodes
const button = document.getElementById('button');
const onClick = () => console.log('clicked');
button.addEventListener('click', onClick);
button.remove(); // Memory leak if listener not removed
// 3. Closures holding references
function outer() {
const largeData = new Array(1000000);
return function inner() {
// largeData is retained even if not used
};
}
// Fix: Set to null when done
let data = { large: new Array(1000000) };
// ... use data
data = null; // Allow garbage collection
Debounce & Throttle
// Debounce: Execute after delay, reset on new calls
function debounce(fn, delay) {
let timeoutId;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn.apply(this, args), delay);
};
}
// Usage: Search input
const search = debounce((query) => {
console.log('Searching:', query);
}, 300);
// Throttle: Execute at most once per interval
function throttle(fn, interval) {
let lastTime = 0;
return function(...args) {
const now = Date.now();
if (now - lastTime >= interval) {
lastTime = now;
fn.apply(this, args);
}
};
}
// Usage: Scroll handler
const handleScroll = throttle(() => {
console.log('Scrolled');
}, 100);
Deep Clone vs Shallow Clone
const original = {
name: "John",
address: { city: "NYC" },
hobbies: ["reading", "gaming"]
};
// Shallow Clone (nested objects share reference)
const shallowClone1 = { ...original };
const shallowClone2 = Object.assign({}, original);
shallowClone1.address.city = "LA";
console.log(original.address.city); // "LA" - mutated!
// Deep Clone methods
// 1. JSON (doesn't handle functions, undefined, symbols, circular refs)
const deepClone1 = JSON.parse(JSON.stringify(original));
// 2. structuredClone (modern, handles circular refs, not functions)
const deepClone2 = structuredClone(original);
// 3. Custom recursive function
function deepClone(obj, seen = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
if (seen.has(obj)) return seen.get(obj);
const clone = Array.isArray(obj) ? [] : {};
seen.set(obj, clone);
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
clone[key] = deepClone(obj[key], seen);
}
}
return clone;
}
Currying
// Transform f(a, b, c) into f(a)(b)(c)
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args);
}
return function(...nextArgs) {
return curried.apply(this, args.concat(nextArgs));
};
};
}
// Example
function add(a, b, c) {
return a + b + c;
}
const curriedAdd = curry(add);
console.log(curriedAdd(1)(2)(3)); // 6
console.log(curriedAdd(1, 2)(3)); // 6
console.log(curriedAdd(1)(2, 3)); // 6
// Practical use case
const log = curry((level, date, message) => {
console.log(`[${level}] ${date}: ${message}`);
});
const errorLog = log('ERROR');
const todayErrorLog = errorLog(new Date().toISOString());
todayErrorLog('Something went wrong');
ES6+ Features
Let, Const & Block Scope
// Temporal Dead Zone (TDZ)
console.log(x); // ReferenceError
let x = 1;
// const with objects
const obj = { a: 1 };
obj.a = 2; // Allowed - modifying property
obj = { b: 2 }; // TypeError - reassigning const
Arrow Functions
// Syntax variations
const fn1 = () => expression;
const fn2 = (a) => expression;
const fn3 = a => expression;
const fn4 = (a, b) => expression;
const fn5 = (a, b) => {
// multiple statements
return result;
};
// No own this, arguments, super, or new.target
const obj = {
values: [1, 2, 3],
double() {
// Arrow inherits 'this' from double()
return this.values.map(v => v * 2);
}
};
// Cannot be used as constructors
const Foo = () => {};
new Foo(); // TypeError
Template Literals
const name = "World";
// Basic
const greeting = `Hello, ${name}!`;
// Multi-line
const html = `
<div>
<h1>${name}</h1>
</div>
`;
// Tagged templates
function highlight(strings, ...values) {
return strings.reduce((result, str, i) => {
return result + str + (values[i] ? `<mark>${values[i]}</mark>` : '');
}, '');
}
const user = "John";
const role = "admin";
highlight`User ${user} has role ${role}`;
// "User <mark>John</mark> has role <mark>admin</mark>"
Destructuring
// Array destructuring
const [a, b, ...rest] = [1, 2, 3, 4, 5];
const [first, , third] = [1, 2, 3]; // Skip elements
const [x = 10] = []; // Default value
// Object destructuring
const { name, age } = { name: "John", age: 30 };
const { name: userName } = { name: "John" }; // Rename
const { address: { city } } = { address: { city: "NYC" } }; // Nested
const { role = "user" } = {}; // Default value
// Function parameters
function greet({ name, age = 18 }) {
console.log(`${name} is ${age}`);
}
// Swapping variables
let a = 1, b = 2;
[a, b] = [b, a];
Spread & Rest Operators
// Spread (expanding)
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5]; // [1, 2, 3, 4, 5]
const obj1 = { a: 1, b: 2 };
const obj2 = { ...obj1, c: 3 }; // { a: 1, b: 2, c: 3 }
Math.max(...arr1); // 3
// Rest (collecting)
function sum(...numbers) {
return numbers.reduce((a, b) => a + b, 0);
}
const [first, ...others] = [1, 2, 3, 4];
const { a, ...remaining } = { a: 1, b: 2, c: 3 };
Classes
class Animal {
// Static property
static kingdom = "Animalia";
// Private field
#id;
constructor(name) {
this.name = name;
this.#id = Math.random();
}
// Instance method
speak() {
console.log(`${this.name} makes a sound`);
}
// Getter
get info() {
return `${this.name} (${this.#id})`;
}
// Setter
set rename(newName) {
this.name = newName;
}
// Static method
static create(name) {
return new Animal(name);
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name);
this.breed = breed;
}
speak() {
console.log(`${this.name} barks`);
}
}
const dog = new Dog("Rex", "German Shepherd");
dog.speak(); // "Rex barks"
Modules
// Named exports
export const PI = 3.14159;
export function add(a, b) { return a + b; }
export class Calculator {}
// Default export
export default function main() {}
// Import named
import { PI, add } from './math.js';
import { add as sum } from './math.js'; // Rename
// Import default
import main from './main.js';
// Import all
import * as math from './math.js';
math.add(1, 2);
// Dynamic import
const module = await import('./module.js');
// Re-export
export { add } from './math.js';
export * from './utils.js';
Symbols
// Create unique identifiers
const id1 = Symbol('id');
const id2 = Symbol('id');
console.log(id1 === id2); // false
// Use as object keys
const obj = {
[id1]: 'value1',
name: 'John'
};
// Not enumerable
Object.keys(obj); // ['name']
Object.getOwnPropertySymbols(obj); // [Symbol(id)]
// Well-known symbols
class MyArray {
static [Symbol.hasInstance](instance) {
return Array.isArray(instance);
}
*[Symbol.iterator]() {
yield 1;
yield 2;
}
}
// Global symbol registry
const globalSym = Symbol.for('global');
Symbol.keyFor(globalSym); // 'global'
Iterators & Generators
// Iterator protocol
const iterable = {
[Symbol.iterator]() {
let count = 0;
return {
next() {
count++;
return count <= 3
? { value: count, done: false }
: { done: true };
}
};
}
};
for (const value of iterable) {
console.log(value); // 1, 2, 3
}
// Generator function
function* numberGenerator() {
yield 1;
yield 2;
yield 3;
}
const gen = numberGenerator();
gen.next(); // { value: 1, done: false }
gen.next(); // { value: 2, done: false }
gen.next(); // { value: 3, done: false }
gen.next(); // { value: undefined, done: true }
// Generator with values
function* fibonacci() {
let [prev, curr] = [0, 1];
while (true) {
yield curr;
[prev, curr] = [curr, prev + curr];
}
}
// Delegating generator
function* combined() {
yield* [1, 2, 3];
yield* "abc";
}
[...combined()]; // [1, 2, 3, 'a', 'b', 'c']
Map & Set
// Map - key-value pairs with any key type
const map = new Map();
map.set('name', 'John');
map.set({ id: 1 }, 'Object key');
map.get('name'); // 'John'
map.has('name'); // true
map.delete('name');
map.size; // 1
map.clear();
// WeakMap - keys must be objects, garbage collected
const weakMap = new WeakMap();
let obj = { id: 1 };
weakMap.set(obj, 'value');
obj = null; // Entry can be garbage collected
// Set - unique values
const set = new Set([1, 2, 2, 3]);
set.add(4);
set.has(3); // true
set.delete(3);
set.size; // 3
// Remove duplicates from array
const unique = [...new Set([1, 2, 2, 3])];
// WeakSet - objects only, garbage collected
const weakSet = new WeakSet();
Optional Chaining & Nullish Coalescing
const user = {
name: "John",
address: {
city: "NYC"
}
};
// Optional chaining (?.)
user?.address?.city; // "NYC"
user?.contact?.phone; // undefined (no error)
user?.getName?.(); // undefined (no error)
user?.hobbies?.[0]; // undefined (no error)
// Nullish coalescing (??)
const value = null ?? "default"; // "default"
const value2 = undefined ?? "default"; // "default"
const value3 = 0 ?? "default"; // 0 (0 is not nullish)
const value4 = "" ?? "default"; // "" (empty string is not nullish)
// Comparison with ||
const value5 = 0 || "default"; // "default" (0 is falsy)
const value6 = 0 ?? "default"; // 0 (0 is not nullish)
New Array Methods (ES2022+)
// Array.at() - negative indexing
const arr = [1, 2, 3, 4, 5];
arr.at(-1); // 5
arr.at(-2); // 4
// Array.findLast() / Array.findLastIndex()
const numbers = [1, 2, 3, 4, 3, 2, 1];
numbers.findLast(n => n > 2); // 3
numbers.findLastIndex(n => n > 2); // 4
// Array.toSorted(), toReversed(), toSpliced() - non-mutating
const sorted = arr.toSorted(); // New sorted array
const reversed = arr.toReversed(); // New reversed array
const spliced = arr.toSpliced(1, 2, 9); // New spliced array
// Array.with() - non-mutating index replacement
const newArr = arr.with(0, 10); // [10, 2, 3, 4, 5]
// Object.groupBy() (ES2024)
const items = [
{ type: 'fruit', name: 'apple' },
{ type: 'fruit', name: 'banana' },
{ type: 'vegetable', name: 'carrot' }
];
Object.groupBy(items, item => item.type);
// { fruit: [...], vegetable: [...] }
Node.js
Node.js Architecture
┌─────────────────────────────────────────────────┐
│ Application │
├─────────────────────────────────────────────────┤
│ Node.js │
│ ┌───────────────┐ ┌────────────────────────┐ │
│ │ V8 Engine │ │ libuv │ │
│ │ (JavaScript) │ │ (Event Loop, Async I/O│ │
│ └───────────────┘ └────────────────────────┘ │
├─────────────────────────────────────────────────┤
│ Operating System │
└─────────────────────────────────────────────────┘
Key Components:
- V8: JavaScript engine (compiles JS to machine code)
- libuv: Cross-platform async I/O library
- Event Loop: Handles async operations
- Thread Pool: For CPU-intensive operations (default: 4 threads)
Event Loop Phases
/*
┌───────────────────────────┐
┌─>│ timers │ (setTimeout, setInterval)
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ pending callbacks │ (I/O callbacks deferred)
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ idle, prepare │ (internal use)
│ └─────────────┬─────────────┘ ┌───────────────┐
│ ┌─────────────┴─────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └─────────────┬─────────────┘ │ data, etc. │
│ ┌─────────────┴─────────────┐ └───────────────┘
│ │ check │ (setImmediate)
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
└──┤ close callbacks │ (socket.on('close'))
└───────────────────────────┘
Between each phase: process.nextTick() and Promises
*/
// Execution order
setTimeout(() => console.log('timeout'), 0);
setImmediate(() => console.log('immediate'));
process.nextTick(() => console.log('nextTick'));
Promise.resolve().then(() => console.log('promise'));
console.log('sync');
// Output: sync, nextTick, promise, timeout, immediate
// (timeout/immediate order may vary)
Modules System
// CommonJS (CJS) - Node.js default
// Synchronous loading, dynamic
const fs = require('fs');
const { readFile } = require('fs');
module.exports = { myFunction };
exports.myFunction = () => {};
// ES Modules (ESM) - Modern
// Async loading, static analysis
import fs from 'fs';
import { readFile } from 'fs';
export const myFunction = () => {};
export default myFunction;
// Use ESM in Node.js:
// 1. Use .mjs extension
// 2. Add "type": "module" in package.json
// Dynamic imports (works in both)
const module = await import('./module.js');
File System
const fs = require('fs');
const fsPromises = require('fs').promises;
// Sync (blocking - avoid in production)
const data = fs.readFileSync('file.txt', 'utf8');
fs.writeFileSync('file.txt', 'content');
// Callback (async)
fs.readFile('file.txt', 'utf8', (err, data) => {
if (err) throw err;
console.log(data);
});
// Promises (modern async)
const data = await fsPromises.readFile('file.txt', 'utf8');
await fsPromises.writeFile('file.txt', 'content');
// Streams (memory efficient for large files)
const readable = fs.createReadStream('large-file.txt');
const writable = fs.createWriteStream('output.txt');
readable.pipe(writable);
readable.on('data', chunk => console.log(chunk));
readable.on('end', () => console.log('Done'));
readable.on('error', err => console.error(err));
Streams
const { Readable, Writable, Transform, pipeline } = require('stream');
const fs = require('fs');
// Readable stream
const readable = new Readable({
read(size) {
this.push('data');
this.push(null); // Signal end
}
});
// Writable stream
const writable = new Writable({
write(chunk, encoding, callback) {
console.log(chunk.toString());
callback();
}
});
// Transform stream
const uppercase = new Transform({
transform(chunk, encoding, callback) {
callback(null, chunk.toString().toUpperCase());
}
});
// Pipeline (handles errors and cleanup)
const { pipeline } = require('stream/promises');
await pipeline(
fs.createReadStream('input.txt'),
uppercase,
fs.createWriteStream('output.txt')
);
// Stream types:
// - Readable: fs.createReadStream, http request
// - Writable: fs.createWriteStream, http response
// - Duplex: net.Socket (both read and write)
// - Transform: zlib.createGzip (modify data)
Events
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {}
const emitter = new MyEmitter();
// Listen to events
emitter.on('data', (arg1, arg2) => {
console.log('data event:', arg1, arg2);
});
// Listen once
emitter.once('init', () => {
console.log('init - only runs once');
});
// Emit events
emitter.emit('data', 'arg1', 'arg2');
emitter.emit('init');
// Remove listener
const handler = () => console.log('handler');
emitter.on('event', handler);
emitter.off('event', handler);
// Error handling
emitter.on('error', (err) => {
console.error('Error:', err);
});
// Get listener count
emitter.listenerCount('data');
// Async iteration (Node.js 10+)
const { on } = require('events');
for await (const [data] of on(emitter, 'data')) {
console.log(data);
}
HTTP/HTTPS
const http = require('http');
const https = require('https');
// Create server
const server = http.createServer((req, res) => {
const { method, url, headers } = req;
// Read request body
let body = '';
req.on('data', chunk => body += chunk);
req.on('end', () => {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ message: 'Hello' }));
});
});
server.listen(3000, () => {
console.log('Server running on port 3000');
});
// Make HTTP request
const options = {
hostname: 'api.example.com',
port: 443,
path: '/users',
method: 'GET',
headers: { 'Content-Type': 'application/json' }
};
const req = https.request(options, (res) => {
let data = '';
res.on('data', chunk => data += chunk);
res.on('end', () => console.log(JSON.parse(data)));
});
req.on('error', console.error);
req.end();
// Using fetch (Node.js 18+)
const response = await fetch('https://api.example.com/users');
const data = await response.json();
Process & Environment
// Environment variables
process.env.NODE_ENV;
process.env.PORT;
// Command line arguments
process.argv; // [node path, script path, ...args]
// Current working directory
process.cwd();
// Exit
process.exit(0); // Success
process.exit(1); // Error
// Signals
process.on('SIGINT', () => {
console.log('Received SIGINT. Graceful shutdown...');
process.exit(0);
});
process.on('uncaughtException', (err) => {
console.error('Uncaught Exception:', err);
process.exit(1);
});
process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled Rejection:', reason);
});
// Memory usage
process.memoryUsage();
// { rss, heapTotal, heapUsed, external, arrayBuffers }
// Next tick
process.nextTick(() => {
// Executes before I/O callbacks
});
Child Processes
const { exec, execSync, spawn, fork } = require('child_process');
// exec - buffered output, shell
exec('ls -la', (error, stdout, stderr) => {
if (error) console.error(error);
console.log(stdout);
});
// execSync - synchronous
const output = execSync('ls -la', { encoding: 'utf8' });
// spawn - streamed output, no shell by default
const child = spawn('ls', ['-la']);
child.stdout.on('data', data => console.log(data.toString()));
child.stderr.on('data', data => console.error(data.toString()));
child.on('close', code => console.log(`Exit code: ${code}`));
// fork - for Node.js scripts, IPC channel
// parent.js
const child = fork('./child.js');
child.send({ type: 'START' });
child.on('message', msg => console.log('From child:', msg));
// child.js
process.on('message', msg => {
console.log('From parent:', msg);
process.send({ type: 'DONE' });
});
Worker Threads
const { Worker, isMainThread, parentPort, workerData } = require('worker_threads');
if (isMainThread) {
// Main thread
const worker = new Worker(__filename, {
workerData: { num: 42 }
});
worker.on('message', result => {
console.log('Result:', result);
});
worker.on('error', console.error);
worker.on('exit', code => {
if (code !== 0) console.error(`Worker stopped with code ${code}`);
});
worker.postMessage('start');
} else {
// Worker thread
parentPort.on('message', msg => {
const result = heavyComputation(workerData.num);
parentPort.postMessage(result);
});
}
// Worker pool for better performance
const { StaticPool } = require('node-worker-threads-pool');
const pool = new StaticPool({
size: 4,
task: (n) => fibonacci(n)
});
const result = await pool.exec(40);
Buffer
// Create buffers
const buf1 = Buffer.alloc(10); // Zero-filled
const buf2 = Buffer.allocUnsafe(10); // Uninitialized (faster)
const buf3 = Buffer.from('Hello'); // From string
const buf4 = Buffer.from([1, 2, 3]); // From array
// Convert
buf3.toString(); // 'Hello'
buf3.toString('base64'); // 'SGVsbG8='
Buffer.from('SGVsbG8=', 'base64').toString(); // 'Hello'
// Operations
buf1.length; // 10
buf1.write('Hi'); // Write string
buf1[0]; // Read byte
Buffer.concat([buf1, buf2]); // Concatenate
buf1.slice(0, 5); // Slice (shared memory!)
buf1.copy(buf2); // Copy
buf1.equals(buf2); // Compare
// JSON
const json = buf3.toJSON();
// { type: 'Buffer', data: [72, 101, 108, 108, 111] }
Async/Await & Promises
Callbacks (Traditional)
// Callback hell / Pyramid of doom
getData(function(a) {
getMoreData(a, function(b) {
getEvenMoreData(b, function(c) {
getYetMoreData(c, function(d) {
console.log(d);
});
});
});
});
// Error-first callback pattern (Node.js convention)
function readFile(path, callback) {
// ... async operation
if (error) {
callback(error, null);
} else {
callback(null, data);
}
}
readFile('file.txt', (err, data) => {
if (err) {
console.error(err);
return;
}
console.log(data);
});
Promises
// Creating a Promise
const promise = new Promise((resolve, reject) => {
// Async operation
setTimeout(() => {
if (success) {
resolve('Success!');
} else {
reject(new Error('Failed'));
}
}, 1000);
});
// Consuming Promises
promise
.then(result => {
console.log(result);
return 'Next value';
})
.then(next => console.log(next))
.catch(error => console.error(error))
.finally(() => console.log('Always runs'));
// Promise states:
// - Pending: initial state
// - Fulfilled: operation completed successfully
// - Rejected: operation failed
// Promise static methods
Promise.resolve(value); // Create fulfilled promise
Promise.reject(error); // Create rejected promise
// All must succeed (fail-fast on first rejection)
Promise.all([p1, p2, p3])
.then(([r1, r2, r3]) => {});
// All settle (never rejects)
Promise.allSettled([p1, p2, p3])
.then(results => {
results.forEach(result => {
if (result.status === 'fulfilled') {
console.log(result.value);
} else {
console.log(result.reason);
}
});
});
// First to settle
Promise.race([p1, p2, p3])
.then(firstResult => {});
// First to fulfill
Promise.any([p1, p2, p3])
.then(firstSuccess => {})
.catch(aggregateError => {}); // All rejected
// Promise.withResolvers() (ES2024)
const { promise, resolve, reject } = Promise.withResolvers();
Async/Await
// Basic syntax
async function fetchData() {
try {
const response = await fetch('/api/data');
const data = await response.json();
return data;
} catch (error) {
console.error('Error:', error);
throw error;
}
}
// Arrow function
const fetchData = async () => {
const data = await fetch('/api/data');
return data.json();
};
// Class method
class API {
async getData() {
return await fetch('/api/data');
}
}
// Parallel execution
async function parallel() {
// Sequential (slow)
const a = await fetchA();
const b = await fetchB();
// Parallel (fast)
const [a, b] = await Promise.all([fetchA(), fetchB()]);
// Parallel with individual handling
const results = await Promise.allSettled([fetchA(), fetchB()]);
}
// Error handling patterns
async function withErrorHandling() {
// Try-catch
try {
await riskyOperation();
} catch (error) {
handleError(error);
}
// Catch on await
const result = await riskyOperation().catch(handleError);
// Optional catch
const [error, data] = await to(riskyOperation());
if (error) handleError(error);
}
// Helper for tuple return
function to(promise) {
return promise
.then(data => [null, data])
.catch(err => [err, null]);
}
// Top-level await (ES2022, ESM only)
const config = await loadConfig();
export { config };
Advanced Async Patterns
// Async iteration
async function* asyncGenerator() {
yield await fetchData(1);
yield await fetchData(2);
yield await fetchData(3);
}
for await (const data of asyncGenerator()) {
console.log(data);
}
// Sequential processing with reduce
const results = await urls.reduce(async (accPromise, url) => {
const acc = await accPromise;
const data = await fetch(url);
return [...acc, data];
}, Promise.resolve([]));
// Concurrent with limit
async function mapWithLimit(items, limit, fn) {
const results = [];
const executing = [];
for (const item of items) {
const promise = fn(item).then(result => {
executing.splice(executing.indexOf(promise), 1);
return result;
});
results.push(promise);
executing.push(promise);
if (executing.length >= limit) {
await Promise.race(executing);
}
}
return Promise.all(results);
}
// Retry pattern
async function retry(fn, retries = 3, delay = 1000) {
for (let i = 0; i < retries; i++) {
try {
return await fn();
} catch (error) {
if (i === retries - 1) throw error;
await new Promise(r => setTimeout(r, delay * (i + 1)));
}
}
}
// Timeout wrapper
function withTimeout(promise, ms) {
const timeout = new Promise((_, reject) => {
setTimeout(() => reject(new Error('Timeout')), ms);
});
return Promise.race([promise, timeout]);
}
// Debounced async function
function debounceAsync(fn, delay) {
let timeoutId;
let pendingPromise = null;
return function(...args) {
if (pendingPromise) {
clearTimeout(timeoutId);
}
return new Promise((resolve, reject) => {
timeoutId = setTimeout(async () => {
try {
const result = await fn.apply(this, args);
resolve(result);
} catch (error) {
reject(error);
}
}, delay);
});
};
}
Converting Callbacks to Promises
const { promisify } = require('util');
const fs = require('fs');
// Using promisify
const readFile = promisify(fs.readFile);
const data = await readFile('file.txt', 'utf8');
// Manual promisification
function promisifiedReadFile(path, options) {
return new Promise((resolve, reject) => {
fs.readFile(path, options, (err, data) => {
if (err) reject(err);
else resolve(data);
});
});
}
// Promisify entire object
const fsPromises = require('fs').promises;
// or
const fs = require('fs/promises');
Interview Questions
JavaScript Questions
Q1: What is the difference between == and ===?
// == performs type coercion, === doesn't
5 == "5" // true
5 === "5" // false
null == undefined // true
null === undefined // false
Q2: Explain closures with an example.
function counter() {
let count = 0;
return function() {
return ++count;
};
}
const increment = counter();
increment(); // 1
increment(); // 2
// count is enclosed in the returned function
Q3: What is event delegation?
// Instead of adding listeners to each child
document.getElementById('list').addEventListener('click', (e) => {
if (e.target.matches('li')) {
console.log(e.target.textContent);
}
});
// Benefits: Better memory, handles dynamic elements
Q4: What is the difference between null and undefined?
// undefined: variable declared but not assigned
let x;
console.log(x); // undefined
// null: intentional absence of value
let y = null;
typeof undefined // "undefined"
typeof null // "object" (historical bug)
Q5: Explain the difference between var, let, and const.
// var: function-scoped, hoisted, can redeclare
// let: block-scoped, TDZ, can reassign
// const: block-scoped, TDZ, cannot reassign (but objects can be mutated)
Q6: What is a pure function?
// Pure: same input = same output, no side effects
const add = (a, b) => a + b;
// Impure: depends on external state or has side effects
let total = 0;
const addToTotal = (n) => total += n;
Q7: What is hoisting?
console.log(x); // undefined (var is hoisted)
console.log(y); // ReferenceError (TDZ)
var x = 1;
let y = 2;
// Function declarations are fully hoisted
foo(); // Works!
function foo() {}
// Function expressions are not
bar(); // TypeError
var bar = function() {};
Q8: What is the prototype chain?
const obj = { a: 1 };
// obj -> Object.prototype -> null
const arr = [1, 2];
// arr -> Array.prototype -> Object.prototype -> null
obj.hasOwnProperty('a'); // Inherited from Object.prototype
Node.js Questions
Q1: What is the event loop and how does it work?
// Single-threaded, non-blocking I/O model
// Phases: timers -> pending -> idle -> poll -> check -> close
// Microtasks run between each phase (nextTick, Promises)
Q2: What is the difference between process.nextTick() and setImmediate()?
// nextTick: runs before I/O callbacks (microtask)
// setImmediate: runs after I/O callbacks (check phase)
process.nextTick(() => console.log('nextTick'));
setImmediate(() => console.log('immediate'));
// Output: nextTick, immediate
Q3: How do you handle uncaught exceptions in Node.js?
process.on('uncaughtException', (err) => {
console.error('Uncaught:', err);
// Graceful shutdown
process.exit(1);
});
process.on('unhandledRejection', (reason) => {
console.error('Unhandled Rejection:', reason);
});
Q4: What are streams and when should you use them?
// Streams handle data piece by piece (chunks)
// Use for: large files, network data, real-time processing
// Types: Readable, Writable, Duplex, Transform
// Memory efficient: doesn't load entire file
const readable = fs.createReadStream('large-file.txt');
readable.pipe(res); // Stream to HTTP response
Q5: Explain the difference between CommonJS and ES Modules.
// CommonJS (CJS): synchronous, dynamic, Node.js default
const module = require('module');
module.exports = {};
// ES Modules (ESM): asynchronous, static, tree-shakeable
import module from 'module';
export default {};
Async/Await Questions
Q1: What happens if you don't await a Promise?
async function example() {
fetchData(); // Returns Promise, doesn't wait
// Next line executes immediately
console.log('Done'); // Runs before fetchData completes
}
Q2: How do you handle errors in async/await?
// Try-catch
try {
await riskyOperation();
} catch (error) {
handleError(error);
}
// Or with .catch()
await riskyOperation().catch(handleError);
Q3: How do you run async operations in parallel?
// Wrong (sequential)
const a = await fetchA();
const b = await fetchB();
// Right (parallel)
const [a, b] = await Promise.all([fetchA(), fetchB()]);
Q4: What is the output of this code?
async function test() {
console.log('1');
await Promise.resolve();
console.log('2');
}
console.log('3');
test();
console.log('4');
// Output: 3, 1, 4, 2
// Explanation:
// - '3' (sync)
// - '1' (sync part of test)
// - '4' (sync)
// - '2' (after await, microtask)
Q5: How do you implement a timeout for an async operation?
function timeout(ms) {
return new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), ms)
);
}
async function fetchWithTimeout(url, ms) {
return Promise.race([
fetch(url),
timeout(ms)
]);
}
Quick Reference Cheat Sheet
Type Checking
typeof "string" // "string"
typeof 42 // "number"
typeof true // "boolean"
typeof undefined // "undefined"
typeof null // "object" (bug!)
typeof {} // "object"
typeof [] // "object"
typeof function(){} // "function"
typeof Symbol() // "symbol"
typeof 42n // "bigint"
Array.isArray([]) // true
obj instanceof Class // prototype check
Object.prototype.toString.call([]) // "[object Array]"
Array Methods Summary
// Mutating: push, pop, shift, unshift, splice, sort, reverse, fill
// Non-mutating: map, filter, reduce, find, some, every, slice, concat
// ES2023: toSorted, toReversed, toSpliced, with
Promise Methods Summary
Promise.all([]) // All succeed or first reject
Promise.allSettled([]) // Wait for all, never rejects
Promise.race([]) // First to settle
Promise.any([]) // First to fulfill
Promise.resolve(v) // Create fulfilled
Promise.reject(e) // Create rejected
Common Gotchas
// 1. typeof null === "object"
// 2. NaN !== NaN (use Number.isNaN())
// 3. 0.1 + 0.2 !== 0.3 (floating point)
// 4. [] + [] === "" (string)
// 5. [] + {} === "[object Object]"
// 6. {} + [] === 0 (block + array)
// 7. Array(3) creates sparse array with length 3
// 8. parseInt("08") might return 0 in old browsers
Last updated: January 2026
Duong Ngo
Full-Stack AI Developer with 12+ years of experience