🟢

Node.js

Event loop, async patterns, NestJS, performance optimization, and best practices.

Event Loop

The Event Loop is the core of Node.js async behavior. Understanding its phases is crucial: 1. **Timers** - Execute setTimeout/setInterval callbacks 2. **Pending callbacks** - Execute I/O callbacks deferred from previous loop 3. **Idle/Prepare** - Internal use only 4. **Poll** - Retrieve new I/O events, execute I/O related callbacks 5. **Check** - Execute setImmediate callbacks 6. **Close callbacks** - Execute close event callbacks **Key Points:** - process.nextTick() runs before any phase - Promises (microtasks) run after each phase - setImmediate vs setTimeout(fn, 0): setImmediate runs in check phase, setTimeout in timers phase
// Event loop example
console.log('1. Start');

setTimeout(() => console.log('2. setTimeout'), 0);
setImmediate(() => console.log('3. setImmediate'));

Promise.resolve().then(() => console.log('4. Promise'));
process.nextTick(() => console.log('5. nextTick'));

console.log('6. End');

// Output: 1, 6, 5, 4, 2, 3 (or 3, 2 may swap)

Async Patterns

**Callbacks → Promises → Async/Await** Modern Node.js uses async/await built on Promises. Key patterns: - **Error handling**: Always wrap async code in try/catch - **Parallel execution**: Use Promise.all() for concurrent operations - **Sequential execution**: Use for...of with await - **Rate limiting**: Use p-limit or custom semaphore
// Parallel vs Sequential
// Parallel - faster when operations are independent
const results = await Promise.all([
  fetchUser(1),
  fetchUser(2),
  fetchUser(3),
]);

// Sequential - when order matters
for (const id of [1, 2, 3]) {
  await processInOrder(id);
}

// With concurrency limit
import pLimit from 'p-limit';
const limit = pLimit(5); // Max 5 concurrent

const tasks = ids.map(id =>
  limit(() => processItem(id))
);
await Promise.all(tasks);

NestJS Essentials

**NestJS** is a progressive Node.js framework using TypeScript and decorators. **Core Concepts:** - **Modules** - Organize code into cohesive blocks - **Controllers** - Handle incoming requests - **Providers/Services** - Business logic, injectable via DI - **Middleware** - Execute before route handlers - **Guards** - Authorization logic - **Interceptors** - Transform/extend behavior - **Pipes** - Data transformation and validation
// NestJS Service with BullMQ
@Injectable()
export class OrderService {
  constructor(
    @InjectQueue('orders') private orderQueue: Queue,
    private readonly prisma: PrismaService,
  ) {}

  async createOrder(dto: CreateOrderDto) {
    const order = await this.prisma.order.create({
      data: dto,
    });

    // Add to queue for async processing
    await this.orderQueue.add('process', {
      orderId: order.id,
    }, {
      attempts: 3,
      backoff: { type: 'exponential', delay: 1000 },
    });

    return order;
  }
}

Performance Tips

**Optimization Strategies:** 1. **Use Streams** for large data - avoid loading everything into memory 2. **Connection pooling** - Reuse database connections 3. **Caching** - Redis for frequently accessed data 4. **Worker Threads** - CPU-intensive tasks off main thread 5. **Cluster mode** - Utilize all CPU cores 6. **Avoid sync operations** - Never block the event loop
// Stream processing example
import { pipeline } from 'stream/promises';
import { createReadStream, createWriteStream } from 'fs';
import { Transform } from 'stream';

const transform = new Transform({
  transform(chunk, encoding, callback) {
    // Process chunk
    this.push(chunk.toString().toUpperCase());
    callback();
  }
});

await pipeline(
  createReadStream('input.txt'),
  transform,
  createWriteStream('output.txt')
);

// Worker threads for CPU tasks
import { Worker } from 'worker_threads';

function runWorker(data) {
  return new Promise((resolve, reject) => {
    const worker = new Worker('./heavy-task.js', {
      workerData: data,
    });
    worker.on('message', resolve);
    worker.on('error', reject);
  });
}

📝 More Node.js content coming soon - Streams, Error handling, Testing strategies...