Zen's Hermitage

Deep Dive into the Differences Between Observers and Event Handlers in Event Processing!

frontendbasefundamentalbasicobserver-vs-event-handler

Overview

What's the difference between observers and event handlers?
What's the difference between observers and event handlers?

Recently, I didn't pay much attention to the differences between observers and event handlers, but I had an experience where I learned a lot about them.

However, there were some ambiguous parts, so I decided to write this article to reorganize my understanding.

Before We Begin...

Let me ask you to think about this for a moment. I expect you've used event handlers quite a bit.

So, when have you used observers?

  • IntersectionObserver for implementing infinite scroll?
  • ResizeObserver for detecting screen size changes?

Then, why did you use these observers?

What advantages did you have compared to using Event Handlers?

Let's think about it once and then dive in!

What is an Observer?

What do you think an observer is?

Have you heard of it somewhere before?

StarCraft Observer
StarCraft Observer

If you've heard of it from StarCraft... you're on the right track haha.

An observer is literally an "Observer".

More precisely, it's a mechanism based on the "Observer Pattern".

So what does it observe here?

The answer is it observes state changes of specific targets.

An observer continuously observes state changes of specific targets and operates by executing pre-registered callback functions when changes are detected.

The key characteristic of observers is asynchronous and continuous observation.

Once registered, callbacks are automatically executed whenever conditions are met, and developers don't need to manually check the state.

It's similar to a security camera that automatically triggers when it detects movement.

Let's take IntersectionObserver as an example:

JavaScript

I think many of you have used this code. It's code that executes a callback when a specific element is detected on screen.

Do you see this part here?

JavaScript

You're telling the observer what to observe. Here, we can see it as detecting state changes of the selected element.

IntersectionObserver observes situations where DOM elements intersect with the viewport or other elements.

It automatically checks the intersection state whenever you scroll or the element's position changes, and executes callbacks when conditions are met.

It's okay if you don't understand. Just keep in mind that this exists haha.

What is an Event Handler?

Event handlers are the core element of event-driven programming.

When specific events like user clicks, keyboard input, or mouse movement occur, they immediately react and execute callback functions.

The key here is "immediately". Please pay attention to this.

The characteristic of event handlers is immediate and reactive execution.

The moment an event occurs, the callback is immediately executed, and event details (which key was pressed, where was clicked, etc.) are passed to the callback function.

JavaScript

I'll briefly mention event handlers and move on since you're probably already familiar with them haha.

Differences Between Event Handlers and Observers

Observer vs Event Handler Callback Execution Comparison

CategoryObserverEvent Handler
Execution TimingExecutes according to browser optimization cycle
Synchronized with rendering frames
Executes immediately when event occurs
Directly connected to user actions
Execution FrequencyBrowser controlled (built-in throttling)
Automatic optimization of duplicate calls
1:1 correspondence with event occurrences
Possible excessive calls during rapid consecutive events
Callback Queue ProcessingIntersection Observer Task Queue
Processed with lower priority
Event Loop's Task Queue
Immediately processed with higher priority
Main Thread BlockingNon-blocking method
Minimal impact on main thread performance
Possibility of blocking
UI responsiveness degradation with complex callbacks
Batch ProcessingMultiple changes bundled into batches
Delivered as entries array
Separate processing for each event
Each event is independent
Browser OptimizationOptimized at browser engine level
Performance benefits of native implementation
Processed at JavaScript engine level
Developer must optimize directly
Resource UsageLow CPU usage
Can utilize GPU acceleration
High CPU usage
Burden especially during high-frequency events
PredictabilityDifficult to predict execution timing
Depends on browser's internal scheduling
Predictable execution timing
Executes simultaneously with event occurrence
Debugging EaseComplex debugging due to asynchronous nature
Difficult to trace callback execution timing
Easy debugging with synchronous execution
Clear event-callback relationship
Memory ManagementUses WeakRef pattern
Supports automatic garbage collection
Requires explicit memory management
Must handle listener removal directly
Error HandlingNo complete observation stop on error
Error isolation at individual entry level
Only affects that specific event on error
No impact on other event processing
Cross-browser BehaviorStandard spec compliant
Browser-specific optimization differences exist
High consistency with older standards
Minimal browser differences

Here's a simple summary.

Getting the idea? I think it might still be vague, so let's look more closely through the performance characteristics comparison below.

Performance Characteristics Comparison

Let's look at the code to make it easier to understand:

JavaScript

How about that? Getting a feel for it? It's okay if not haha.

Now let's explore each one in detail.

Deep Dive into Event Handlers

Let's first examine event handlers.

Essential Characteristics of Event Handlers

To understand event handlers, you first need to grasp the philosophy of event-driven programming.

This is the core concept of reactive programming: when something happens, react immediately.

Just like a doorman immediately opens the door when someone knocks, event handlers receive specific signals (events) and immediately perform predetermined actions (callbacks).

Here, the concept of "immediately" is very important. The moment a user clicks a button, the browser detects that click, finds the registered event handler, and executes it right away.

There's no delay or wait time for optimization in this process.

JavaScript

Relationship with the Event Loop

To understand how event handlers execute, you need to understand JavaScript's Event Loop.

This is the core mechanism showing how single-threaded JavaScript handles asynchronous tasks.

When a user clicks, the browser immediately puts that event into the Task Queue.

Once the currently running code finishes, the event loop immediately takes the event from the task queue and executes the corresponding handler.

Because it has high priority in this process, it's processed before other tasks.

JavaScript

Problems with High Execution Frequency

The "immediate execution" characteristic of event handlers sometimes causes performance issues.

This problem is particularly prominent with high-frequency events.

Events like scroll or mouse movement can occur dozens to hundreds of times per second, and if callbacks execute every time, the browser can struggle.

JavaScript

To solve this problem, developers use techniques like Throttling or Debouncing.

But this also shows the limitations of event handlers themselves.

JavaScript


Pros and Cons of Synchronous Execution

Event handlers execute synchronously. This means when an event occurs, all other JavaScript code pauses momentarily while the handler is processed first.

This characteristic is like a double-edged sword.

The advantage is predictability. When a user clicks a button, the handler executes at exactly that moment, improving UI responsiveness.

Also, when debugging, the causal relationship between event occurrence and handler execution is clearly visible.

JavaScript

But there are also disadvantages. If you perform long-running tasks inside the handler, the entire page may appear frozen. This greatly harms user experience.

JavaScript

Understanding these characteristics of event handlers helps you understand why observers are a better choice in certain situations. Observers are a different approach designed to solve these problems at the browser level.

What is Synchronous Execution?

Before going deeper, let's clearly understand what "synchronous execution" is.

Usually when we say synchronous execution, we think of code running line by line in order. However, in reality, it doesn't simply mean "executes in order".

More precisely, it means "only one task is performed at a time, and all other tasks wait until the current task completely finishes".

Let's think about it. Imagine you're reading a book and someone starts talking to you. You completely stop reading and focus on that person's words, then start reading again when the conversation ends.

This is exactly how synchronous execution works. Not doing two things at the same time, but processing one completely at a time.

JavaScript

In this example, when the user clicks the button, all other JavaScript tasks stop until the click event handler completely finishes.

Other clicks, timer callbacks, network response processing - everything must wait.

The True Meaning of Predictability

The biggest advantage of synchronous execution is predictability. But this doesn't simply mean "you can predict the execution order".

More importantly, it guarantees state consistency.

For example, imagine a user clicked a "Save" button. Several tasks might be needed at this moment.

You might need to validate form data, update the UI, and send a request to the server. In synchronous execution, these tasks are processed atomically.

JavaScript

What would happen if this process could be interrupted in the middle?

When a user clicks rapidly multiple times, a state could occur where form validation completed but UI wasn't updated. Such intermediate states easily become sources of bugs.

Psychological Effect of Immediate Feedback

The immediate feedback provided by synchronous execution is core to user experience beyond just technical benefits.

The human brain is very sensitive to delays between action and result.

Research shows users feel "immediate" at response times under 100 milliseconds, and perceive it as "slow" beyond 1 second.

JavaScript

Such immediate feedback makes users feel the interface is "alive".

Buttons responding immediately when pressed, highlights appearing right away on hover - these are all benefits of synchronous execution.

Clear Causal Relationships in Debugging

From a developer's perspective, one of the biggest advantages of synchronous execution is ease of debugging.

Because the process of events occurring and handlers executing is clear and predictable, finding the cause when problems occur is relatively easy.

JavaScript

In contrast, asynchronously processed tasks have unpredictable execution order and create much more complex situations when debugging.

Real Impact of Main Thread Blocking

However, the disadvantages of synchronous execution are also clear. The most serious problem is main thread blocking. Let's understand this concretely.

JavaScript runs on the same thread as the UI thread. This means while JavaScript code executes, all UI-related work like screen rendering, user input processing, and animations stops. This is exactly what "blocking" means.

JavaScript

In such situations, users feel "the website is slow" or "there's a bug". This problem is especially prominent on mobile devices.

Cumulative Effect of Performance Issues

Main thread blocking isn't just a problem for that moment.

There's a cumulative effect. Browsers aim for 60 frames per second, meaning they need to redraw the screen every 16.67 milliseconds.

If an event handler runs longer than this time, frame drops occur.

JavaScript

Such problems cause serious user experience degradation especially in web applications with lots of interaction. In games, graphics editors, real-time charts, etc., these performance issues can be fatal.

Solution Strategies

Developers use several strategies to overcome the disadvantages of synchronous execution. The most basic is task splitting.

JavaScript

This way, you can maintain the advantages of synchronous execution while minimizing the disadvantages. But this isn't a fundamental solution either.

Because of these limitations, new APIs like IntersectionObserver emerged.

Deep Dive into the Observer Pattern

Now that we've deeply understood the synchronous execution method of event handlers, let's enter the world of observers.

We already looked at what the observer pattern is earlier, but think of this as a session going deeper.

Understanding observers is like learning a completely different way of thinking. If event handlers are an imperative approach of "when something happens, react immediately", observers are a declarative approach of "watch something and let me know when there's a change".

To understand observers, you first need to grasp their philosophical foundation.

In event handlers, developers actively decide "when to check what".

For example, listening to scroll events and checking element positions at each moment.

But in observers, you only define "what to observe", and leave "when to check" to the browser.

It's like the difference between a security guard and CCTV.

A security guard (event handler) must patrol and check situations directly, but CCTV (observer) just needs to be installed and automatically monitors then notifies when problems occur.

CCTV is more efficient because it can observe continuously 24 hours, monitor multiple places simultaneously, and only sends notifications when actual problems occur.

JavaScript

Synchronization with Browser Optimization Cycles

One of the most important characteristics of observers is being synchronized with the browser's rendering cycle.

This is a very important concept, and to understand it you need to know how browsers draw the screen.

Browsers generally update the screen at 60 frames per second.

This means every approximately 16.67 milliseconds, the following process occurs:

First JavaScript execution, then style calculation, layout calculation, paint, and composite in order. IntersectionObserver's callback executes at a specific point in this rendering pipeline.

JavaScript

Thanks to this synchronization, observers can provide accurate information.

Because callbacks execute after the browser has finished all calculations, the information developers receive is the reliable latest state.

Efficiency of Batch Processing

Another reason observers are more efficient than event handlers is the batch processing method.

Even if multiple elements' states change simultaneously, observers bundle them into a single callback call. This provides significant performance benefits.

Think about it. If there are 100 images on a page and a user scrolls quickly, what happens?

With the event handler approach, every time a scroll event occurs, you must check all 100 images' positions.

But with the observer approach, the browser collects only "images that changed in this frame" and notifies you all at once.

JavaScript

The notable point in this example is that no matter how fast the user scrolls, only one callback executes maximum per rendering frame.

If 10 images appeared on screen simultaneously in one frame, instead of 10 individual calls, one callback executes with an array containing 10 elements.

Meaning of Non-blocking Execution

Another key characteristic of observers is non-blocking execution.

This goes beyond simply meaning "doesn't block the main thread", showing a more fundamental difference in execution philosophy.

In event handlers, all other tasks must wait while the handler function executes.

But in observers, while the callback function itself still executes on the main thread, the observation task itself is handled by other systems in the browser.

This means that no matter how many observation targets there are, it barely affects JavaScript execution.

JavaScript

Browser Engine Level Optimization

One reason observers are powerful is they're optimized at the browser engine level.

This provides optimization levels difficult to implement in JavaScript.

For example, browsers can accelerate transformation matrix calculations using the GPU.

Also, browsers can skip unnecessary calculations for elements that aren't actually visible on screen.

These optimizations utilize browser-internal information not accessible at the JavaScript level.

JavaScript

Automation of Memory Management

Observers also take a different approach from event handlers in terms of memory management.

Observers internally use the WeakRef pattern, so when an observed element is removed from the DOM, the corresponding observation is automatically cleaned up.

This is a great help in preventing memory leaks.

JavaScript

We've now looked at the basic characteristics of observers.

Can you now see the differences from event handlers more clearly?

Browser Rendering Pipeline and Observers

Let's look at when observers execute in the browser rendering pass.

Understanding execution timing in the rendering pass is like knowing when each instrument plays in an orchestra.

Each observer executes at different points in the browser's rendering pipeline, and understanding these points helps you know why specific observers are suitable for specific tasks.

Understanding the Browser Rendering Pipeline

Let's look step-by-step at how a browser renders one frame.

You need to understand this process to clearly grasp at which stage each observer executes.

The browser's rendering pipeline generally executes in the following order:

First, in the JavaScript execution stage, user code and event handlers execute.

Next, in the style calculation stage, CSS rules are applied to determine each element's final style.

Then in the layout stage, each element's exact position and size are calculated.

In the paint stage, actual pixel data is generated, and finally in the composite stage, multiple layers are combined to create the final screen.

Each observer executes at different points in this pipeline, which determines the information they can access and their performance characteristics.

Think about it. An observer that executes before layout is calculated can't get accurate position information, but observers that execute after layout can provide accurate information.

IntersectionObserver: Executes After Layout

IntersectionObserver is the most widely known observer, and executes after layout calculation is complete.

This is a very important characteristic because it needs accurate element position and size information.

JavaScript

That IntersectionObserver executes after layout has significant performance implications.

If you directly call getBoundingClientRect() in JavaScript, the browser must forcefully calculate layout immediately.

But IntersectionObserver executes after the browser naturally calculates layout, preventing unnecessary layout recalculations.

ResizeObserver: Executes Right After Layout

ResizeObserver is an observer that detects element size changes.

It executes at a similar time to IntersectionObserver, but can execute at a slightly earlier point.

This is because ResizeObserver is very closely connected to the layout calculation stage.

JavaScript

One interesting characteristic of ResizeObserver is that it has a mechanism to prevent infinite loops.

If you change the observed target's size inside the ResizeObserver callback, the browser detects this and handles it appropriately.

This allows safe implementation of complex responsive layouts.

MutationObserver: Executes Immediately on DOM Changes

MutationObserver has a completely different execution timing from other observers.

Since it detects DOM structure changes, it executes at the earliest stage of the rendering pipeline.

Specifically, it executes right after DOM changes occur, but before style calculation or layout calculation begins.

JavaScript

That MutationObserver executes at an early stage has important implications.

This enables immediate reaction to DOM structure changes, but also means final layout information isn't available yet.

Therefore MutationObserver is mainly used for managing DOM structure itself.

PerformanceObserver: Performance Measurement Specialist

PerformanceObserver is a special observer that observes browser performance metrics.

Since it provides performance data collected from various points in the rendering pipeline, it has a completely different execution pattern from other observers.

JavaScript

PerformanceObserver is very useful for monitoring web application performance in real-time.

It's an essential tool especially when measuring user experience metrics like Core Web Vitals.

Execution Order and Interaction of Observers

When multiple observers are active simultaneously, in what order does the browser execute them?

This is directly related to which point in the rendering pipeline each observer executes.

JavaScript

Running this example, you can confirm that observers execute according to the browser's rendering pipeline order.

Understanding this execution order allows you to implement predictable behavior even in applications with complex interactions.

Summary

This has been quite a long article.

Through this topic, I hope you were able to learn in detail about the differences between event handlers and observers, and at what timing observers execute in the browser rendering pipeline.

If you have any questions, please leave a comment and I'll reply as soon as I see it!

Thank you for reading this long article. 🙇