The Doorbell Problem
Imagine you are expecting a courier to deliver a parcel sometime today, but you don't know exactly when. There are two ways you could handle this.
Method A: Every 30 seconds, you walk to the front door, open it, look outside to check if the courier has arrived, and if not, walk back inside. You repeat this all day — thousands of trips to the door — just in case the courier showed up in between your checks.
Method B: You sit and do whatever you want — homework, watching TV, coding — and you only walk to the door when the doorbell actually rings. The doorbell notifies you the instant something worth reacting to happens.
Nobody uses Method A in real life, because it wastes enormous effort checking a door that is usually empty, and it can still miss the courier if they arrive in the gap between two checks. Everybody uses Method B, because it reacts instantly and wastes zero effort while waiting.
This is exactly the difference between two styles of writing computer programs. Method A is called polling — repeatedly checking "has anything happened yet?" in a loop. Method B is called event-driven programming — the program sits idle and only runs a specific block of code the instant a specific thing happens. This chapter is about how event-driven programs are built, how they actually run inside the computer, and where you meet them every day.
Two Ways to Write a Program
Most of the very first programs you write look like a recipe: step 1, then step 2, then step 3, in a fixed order decided entirely by you, the programmer. This is called procedural (or sequential) programming. Consider this simple program:
name = input("Enter your name: ")
marks = int(input("Enter your marks: "))
if marks >= 90:
print(name + ", you scored a distinction!")
else:
print(name + ", keep practicing!")
print("Program finished.")
This program runs top to bottom, once, and then stops. It asks exactly one question, at exactly one moment, in exactly the order you wrote it. There is no ambiguity about what runs next — line 2 always runs right after line 1.
Now think about an app like a UPI payment app, a WhatsApp chat screen, or a railway ticket booking website. These programs cannot work this way, because they don't know in what order the user will do things. A user might tap "Send Money" first, or might tap "Scan QR" first, or might receive an incoming message before doing either. The program has to sit ready and respond correctly no matter which of many possible things happens, and in whatever order they happen. That is what event-driven programming is built for: code that reacts to things happening, rather than code that marches through a fixed sequence you decided in advance.
What Exactly Is an "Event"?
An event is a signal that something specific has happened, at a moment the program could not have predicted in advance. Events generally come from three sources:
- User-generated events — a button click, a screen tap, a key press, a swipe, dragging an item.
- System-generated events — a timer running out, a file finishing its download, a battery reaching 15%.
- External/device events — a message arriving from the network, a sensor detecting motion, a temperature crossing a threshold.
Whenever one of these things occurs, the running program needs a way to say: "if this particular event happens, run this particular piece of code." That piece of code is called an event handler (also called a callback function, because the system "calls it back" later, whenever the event actually occurs).
Worked Example: Registering an Event Handler
Let's trace through a small, real, working example — a webpage with a button that books a ticket. This uses JavaScript, the language browsers run, because it is the clearest place to see event-driven code in action.
<button id="bookBtn">Book Ticket</button>
<script>
let clickCount = 0;
function handleClick() {
clickCount = clickCount + 1;
console.log("Ticket booking attempt #" + clickCount);
}
document.getElementById("bookBtn").addEventListener("click", handleClick);
console.log("Setup complete. Waiting for a click...");
</script>
Let's trace this line by line, the way an examiner would want you to:
- The browser reads the HTML and creates the button on the screen.
- The browser reads the script top to bottom, exactly once. First,
clickCountis created and set to 0. - Next, the function
handleClickis defined — but defining a function does not run it. This is a crucial point: the code insidehandleClickdoes not execute yet. addEventListener("click", handleClick)is executed. This does not click anything and does not runhandleClick. It only registers a promise with the browser: "when a click happens on this button, please call this function."console.log("Setup complete...")runs immediately. So the very first, and for a while the only, output printed is"Setup complete. Waiting for a click..."— even thoughhandleClickwas written earlier in the file.- The script has now finished running from top to bottom. The browser does not shut down. It keeps the page alive and waits.
- Sometime later — one second later, or one hour later, or never — the user clicks the button. Only at that instant does the browser look up which function was registered for "click" on this button, and calls
handleClick(). - Inside
handleClick,clickCountbecomes 1, and"Ticket booking attempt #1"is printed. - If the user clicks again, the whole handler runs again from the top, and
clickCountbecomes 2.
Notice the strange but important fact: the function handleClick is written in the middle of the file, but it might run zero times, once, or a hundred times, at completely unpredictable moments — while the "Setup complete" line, written after it, always runs exactly once, immediately. In event-driven code, the order code is written in is not the order it runs in. Only the setup code runs in written order; the handlers run in event order, decided at runtime by the user or the system.
Multiple Events: Order Is Decided at Runtime
Now suppose the page has two buttons:
<button id="payBtn">Pay Now</button>
<button id="cancelBtn">Cancel</button>
<script>
function onPay() { console.log("Payment started"); }
function onCancel() { console.log("Order cancelled"); }
document.getElementById("payBtn").addEventListener("click", onPay);
document.getElementById("cancelBtn").addEventListener("click", onCancel);
</script>
onPay is registered first in the code. But if the user's finger happens to tap "Cancel" before ever touching "Pay Now," then "Order cancelled" prints first, and onPay might never print at all in that session. If a different user taps "Pay Now" three times and never touches "Cancel," you only ever see "Payment started", three times in a row. The order handlers run in, and how many times each one runs, is decided entirely by which events actually occur and in what order — never by the order they were typed in the source file.
Correcting a Common Misconception
A mistake many students make when they first meet event-driven code is assuming it behaves like a procedural program — that if onPay is written above onCancel, then onPay's message must print above onCancel's message when the program runs, just like line 2 always prints after line 1 in a normal sequential script. This is false. Only the one-time setup code (the part that registers the handlers) runs in written order. The handler functions themselves are dormant code — like a fire alarm sitting silently on a wall — that only spring to life when their specific event actually occurs, in whatever order those events happen to occur in the real world. Two students running the exact same code, clicking buttons in a different order, will see the console print completely different sequences of messages.
Under the Surface: The Event Loop
How does the browser (or any event-driven system) actually manage this waiting-and-reacting behaviour? It uses a mechanism called the event loop, built from three moving parts:
- The call stack — where code is actively executing right now, one function at a time.
- The event queue — a waiting line (first-in, first-out) holding events that have occurred but whose handlers haven't run yet.
- The event loop itself — a continuously running checker that asks, over and over: "Is the call stack empty? If yes, take the next event out of the queue and run its handler."
If two events happen very close together — say the user clicks a button while a 5-second timer also fires — both events get placed into the queue in the order they occurred. The currently running handler (if any) is allowed to finish completely before the next one starts. This is why event-driven programs like JavaScript in a browser generally run one handler at a time, from start to finish, never interrupting a handler halfway through to run another one.
Event-Driven Ideas Outside the Browser: Interrupts
Event-driven thinking isn't limited to websites — it also shows up at the hardware level, in physical computing. Suppose you connect a push-button to a microcontroller (like an Arduino) and want it to ring a bell when pressed. There are two ways to write this, mirroring the doorbell problem from the start of this chapter.
Polling style — the CPU keeps asking, over and over, thousands of times per second, whether the button is currently pressed:
void loop() {
if (digitalRead(buttonPin) == HIGH) {
ringBell();
}
delay(10); // check again in 10 milliseconds, forever
}
This works, but the CPU is constantly busy checking a button that is almost always not pressed — it can't fully use that time for anything else, and if it's doing something else for even 10 milliseconds, a very quick press could be missed.
Event-driven (interrupt) style — the CPU registers a handler once and is then free to do other work; special hardware circuitry notifies ("interrupts") the CPU the instant the button's voltage changes:
attachInterrupt(buttonPin, ringBell, RISING);
// The CPU is now free to run other code.
// ringBell() is called automatically, only at the exact
// instant the button is pressed — never checked "just in case."
This hardware mechanism is called an interrupt, and it is the same core idea as a browser's addEventListener: register a handler once, then let the system notify you only when something real happens, instead of wasting cycles asking again and again.
Where You Meet This Every Day
Once you know what to look for, event-driven systems are everywhere around you:
- A cricket score app doesn't ask the server "any wicket yet? any wicket yet?" every second — that would drain your battery and flood the network. Instead, the server pushes an event to your phone the moment a wicket actually falls, and your app's update-handler runs instantly, showing the new score.
- A UPI payment app registers a handler for "payment result received." Your phone doesn't freeze checking in a loop while the banks talk to each other in the background — the handler is simply called back automatically once the result event arrives, whether that takes one second or ten.
- Typing on a keyboard triggers a keypress event for every single character, each one calling a handler that adds that character to the text box on screen.
- An elevator button doesn't run a loop asking "was I pressed? was I pressed?" — pressing it triggers an event that the elevator's control system responds to whenever it occurs, in whatever order across floors people happen to press buttons.
Procedural vs Event-Driven: A Direct Comparison
It helps to state the contrast plainly, because CBSE-style questions often test this distinction directly:
- Procedural programming: Execution order is fixed by the programmer at the time of writing the code. The program runs from a defined start to a defined end, once, and then stops.
- Event-driven programming: Execution order of the handler functions is decided at runtime, by whichever events actually occur, in whatever order the user or the system produces them. The program's setup code runs once, but the program as a whole doesn't have a fixed "end" — it stays alive, ready to react, until the user closes it or the device shuts down.
Note that these aren't mutually exclusive: the code inside a single event handler is usually written procedurally — line 1, then line 2, then line 3, exactly like the marks-and-grade example earlier. What makes the overall program event-driven is that which handler runs, and when, is decided by events, not by the programmer typing a fixed sequence.
Practice: Active Recall
- In the two-button "Pay Now" / "Cancel" example, if a user clicks "Cancel," then "Pay Now," then "Cancel" again, write out the exact three lines that get printed to the console, in order.
- A classmate says: "Since
onPayis written beforeonCancelin the code,onPaymust always run first." Explain in one or two sentences why this is incorrect. - Identify whether each of the following is a user-generated event, a system-generated event, or an external/device event: (a) a phone's battery hitting 5%, (b) pressing the volume-down button, (c) a temperature sensor crossing 40°C, (d) a downloaded file completing.
- Rewrite this polling-style pseudocode as an event-driven design, describing what handler you would register and for what event:
"Every second, check if a new WhatsApp message has arrived; if yes, show a notification." - In the event loop diagram, explain in your own words why a handler that takes a very long time to finish (say, 5 full seconds of heavy calculation) would cause every other queued event — even a simple button click — to wait those 5 seconds before its own handler can start.
- True or False, with a one-line justification: "An event-driven program never contains any procedural (line 1, then line 2) code."
Summary
- Polling means repeatedly checking "did something happen?" in a loop; it wastes effort and can miss fast events. Event-driven means registering a handler once and letting the system call it only when the specific event actually occurs.
- An event is a signal that something happened — user-generated (click, keypress), system-generated (timer), or external (message, sensor).
- An event handler (callback function) is defined and registered once during setup, but its code only runs later, at the unpredictable moment its event occurs — possibly zero times, once, or many times.
- Only the one-time setup code runs in the exact order it is written; handler execution order is decided entirely by the order events actually occur at runtime — this is the key misconception to avoid.
- The event loop mechanism — event queue, call stack, and the loop itself — is what lets a program stay idle, then react, one handler at a time, running each to completion before starting the next.
- The same idea appears in hardware as interrupts:
attachInterrupt()lets a microcontroller react to a button press without constantly pollingdigitalRead()in a loop. - Event-driven systems are the backbone of apps you use daily — score updates, payment confirmations, chat notifications — precisely because they react instantly without wasting resources checking "did anything happen?" over and over.