Glossary of Terms

Mastering Key Programming Concepts: From OOP to Event-Driven Programming

By the Domain India teamPublished 8 min read
Knowledge base article
Contents (8 sections)

Most real JavaScript applications use four programming styles at once: object-oriented code to model things, functional code to transform data, asynchronous code to wait for networks and disks, and event-driven code to react to users and messages. Knowing which one fits which job makes code easier to read and debug. This guide explains all four with short, current JavaScript examples for Node.js and the browser.

Key takeaways

OOP groups data and behaviour into classes (use #private fields and prefer composition to deep inheritance). Functional programming uses pure functions and immutable data (map, filter, reduce, spread). Asynchronous programming uses promises and async/await so slow work doesn't block. Event-driven programming reacts to events through listeners and the event loop. Good applications combine all four.

1. Why paradigms matter

A paradigm is a way of organising code. None is "best"; each solves a different problem:

ParadigmOrganises code aroundTypical use
Object-orientedObjects that hold state and behaviourDomain models, UI components, services
FunctionalPure functions and immutable dataData transformation, validation, reducers
AsynchronousWork that finishes laterNetwork calls, database queries, file I/O
Event-drivenThings that happenClicks, messages, webhooks, streams

JavaScript supports all four natively, which is why it helps to understand each one separately before mixing them.

2. Object-oriented programming

A class is a blueprint; an object is an instance with its own data. Modern JavaScript supports truly private fields with #:

javascript
class Employee {
  #salary;

  constructor(name, salary) {
    this.name = name;
    this.#salary = salary;
  }

  get monthlyPay() {
    return Math.round(this.#salary / 12);
  }

  work() {
    return `${this.name} does general work.`;
  }
}

class Manager extends Employee {
  constructor(name, salary, department) {
    super(name, salary);
    this.department = department;
  }

  work() {
    return `${this.name} manages ${this.department}.`;
  }
}

const staff = [new Employee('Asha', 600000), new Manager('Ravi', 1200000, 'Sales')];
staff.forEach((p) => console.log(p.work()));

The four classic principles, in plain terms:

Encapsulation
Keep an object's data private and expose only methods. #salary can't be read or changed from outside.
Inheritance
A class can extend another and reuse its code. Manager extends Employee.
Polymorphism
Different classes answer the same method call in their own way. Each object's work() differs, and the loop doesn't care which is which.
Abstraction
Callers use a simple interface (monthlyPay) without knowing the details behind it.
Prefer composition to deep inheritance

Long inheritance chains become fragile: a change in a parent class ripples through every child. Often it's clearer for an object to have helpers (a Mailer, a PriceCalculator) than to be a subclass of something. Use extends for genuine "is a" relationships only.

3. Functional programming

Functional code avoids changing shared data. Functions take input and return new output:

javascript
// Pure: same input, same output, no side effects
const withGst = (amount) => Math.round(amount * 1.18);

// Immutability: copy and change, don't mutate
const order = { id: 7, status: 'pending' };
const paid = { ...order, status: 'paid' };   // order is unchanged

// Higher-order functions
const orders = [
  { id: 1, total: 1200, paid: true },
  { id: 2, total: 450, paid: false },
  { id: 3, total: 3000, paid: true },
];

const paidTotal = orders
  .filter((o) => o.paid)
  .map((o) => o.total)
  .reduce((sum, t) => sum + t, 0); // 4200

Newer array methods return copies instead of changing the original: toSorted(), toReversed(), toSpliced() and with(). Use them instead of sort() and reverse(), which change the array in place. structuredClone() makes a deep copy of plain data.

4. Asynchronous programming

JavaScript runs your code on one thread. Slow operations, such as an HTTP request or a database query, run in the background, and your code continues when they finish. There are three generations of syntax.

Callbacks came first. They nest badly when one step depends on another ("callback hell"), so use them only where an API requires them.

Promises represent a value that will arrive later:

javascript
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

wait(500).then(() => console.log('half a second later'));

async/await is the modern way to write promise-based code so it reads top to bottom:

javascript
async function getUser(id) {
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), 5000); // 5-second timeout

  try {
    const res = await fetch(`https://api.example.com/users/${id}`, {
      signal: controller.signal,
    });
    if (!res.ok) throw new Error(`HTTP ${res.status}`);
    return await res.json();
  } finally {
    clearTimeout(timer);
  }
}

fetch is built into browsers and into current Node.js, so you don't need a library for simple HTTP calls. Note that fetch only rejects on network errors, not on HTTP errors, which is why the code checks res.ok.

Run independent tasks in parallel instead of one after another:

javascript
const [user, orders] = await Promise.all([getUser(1), getOrders(1)]);

// Keep going even if some fail
const results = await Promise.allSettled(ids.map(getUser));
Never leave a promise unhandled

Every await should sit inside a try/catch somewhere up the call chain, and every .then() chain needs a .catch(). In current Node.js versions an unhandled promise rejection stops the process by default.

5. Event-driven programming

In event-driven code, you register listeners and the program runs them when something happens. In the browser:

javascript
document.querySelector('#buy').addEventListener('click', (event) => {
  event.preventDefault();
  console.log('Buy button clicked');
});

In Node.js, many core objects (servers, streams, sockets) are event emitters, and you can create your own:

javascript
import { EventEmitter } from 'node:events';

const shop = new EventEmitter();

shop.on('order:paid', (order) => console.log(`Send receipt for #${order.id}`));
shop.on('order:paid', (order) => console.log(`Start delivery for #${order.id}`));

shop.emit('order:paid', { id: 42 });

The code that emits order:paid doesn't need to know who listens. That loose coupling is the main benefit: you can add a new reaction, such as an SMS, without touching the payment code.

How the event loop runs it all

The event loop is what makes async and event-driven code work together. It runs your synchronous code first, then microtasks (promise callbacks), then the next task from the queue (timers, I/O, events), and repeats:

javascript
console.log('1 sync');
setTimeout(() => console.log('4 timer task'), 0);
Promise.resolve().then(() => console.log('3 microtask'));
console.log('2 sync');
// Prints 1, 2, 3, 4

Long synchronous work, such as a big loop, blocks the loop, and nothing else runs until it finishes: no clicks, no requests. Move heavy CPU work to a Worker (browser) or worker_threads (Node.js).

6. Combining the four

A typical order flow uses all of them:

  1. An Order class holds the order's data and rules (OOP).
  2. Pure functions calculate totals, GST and discounts (functional).
  3. The payment API is called with async/await (asynchronous).
  4. On success, an order:paid event triggers the receipt, the stock update and the delivery request (event-driven).

7. Running JavaScript on Domain India

Use a current long-term-support (LTS) release of Node.js, such as Node.js 24, and upgrade before your version reaches end of life.

  • Shared cPanel hosting: the Setup Node.js App tool offers Node.js 20, 22 and 24 on our cPanel servers. Choose 22 or 24, because Node.js 20 is past its end of life. See deploy a Node.js app on shared hosting.
  • App Platform: Node.js apps are detected and built automatically; anything else needs a Dockerfile. The App Platform doesn't support WebSockets, so for real-time features such as live chat, use a VPS. Start with App Platform getting started.
  • VPS: full root control for any Node.js setup, including WebSockets and background workers. VPS plans are self-managed.
App Starter
₹100/mo + GST
  • 512 MB RAM per app
  • 1 vCPU
  • 5 GB NVMe SSD
  • PostgreSQL Database
See plan details

The price on the card is a live Domain India list price and excludes 18% GST. For the same ideas in PHP, see asynchronous programming in PHP and event-driven programming in PHP.

What are the main programming paradigms in JavaScript?

JavaScript supports object-oriented programming with classes, functional programming with pure functions and array methods, asynchronous programming with promises and async/await, and event-driven programming with event listeners and emitters. Most applications use all four.

What is the difference between asynchronous and event-driven programming?

Asynchronous programming is about work that finishes later, such as a network request, and continuing when it does. Event-driven programming is about reacting to things that happen, such as a click or a message. Both rely on JavaScript's event loop.

Should I use callbacks, promises or async/await?

Use async/await for new code. It is built on promises and reads top to bottom. Use Promise.all or Promise.allSettled to run independent tasks in parallel, and callbacks only where an older API requires them.

How do I make a field private in a JavaScript class?

Prefix it with #, for example #salary. Private fields can only be read or changed inside the class body.

Why does my Node.js process stop on a rejected promise?

Current Node.js versions end the process on an unhandled promise rejection by default. Wrap await calls in try/catch and add .catch() to promise chains.

Can I run Node.js apps on Domain India shared hosting?

Yes, on cPanel hosting through Setup Node.js App, which offers Node.js 20, 22 and 24. For apps that need WebSockets or background workers, use a VPS.

Ready to build? Try your app on the App Platform, host it with cPanel hosting, or take full control with a VPS.

Deploy your JavaScript app

Node.js apps are detected and built automatically, with free SSL and your own domain.

See the App Platform

Was this article helpful?

Your answer helps us decide what to improve next.

Still need help? Open a support ticket and our team will reply.

Prefer an app? Add this site to your home screen.Get the app
OOP to Event-Driven Programming in JavaScript | Domain India