Create your own
Lesson illustration

Time-Based and Deferred Observables

Hello! Welcome back to our course on RxJS.

In our last lesson, we established a solid foundation by learning to create Observables from static data (of), iterables and Promises (from), and DOM events (fromEvent). We also reinforced the crucial distinction between cold (unicast) and hot (multicast) Observables.

Today, we'll continue exploring creation operators, focusing on those that are governed by time. This is essential for handling tasks like polling, animations, and delayed actions in a reactive way. We will also look at a powerful utility operator that gives you precise control over when your Observable's logic is executed.

Our learning outcome for this lesson is to generate time-based and deferred Observables using interval, timer, and defer.

Let's begin.

1. Periodic Emissions with interval

In front-end development, you're certainly familiar with window.setInterval() for executing code repeatedly at a fixed time delay. The interval creation function is its reactive equivalent.

interval(period) creates an Observable that emits an ever-increasing sequence of numbers (0, 1, 2, ...), with each emission separated by the specified time period in milliseconds.

The first value is emitted after the first period has passed. For example, interval(1000) will wait one second, emit 0, wait another second, emit 1, and so on.

This type of Observable never completes on its own. This has a critical implication for your work as a developer: you must unsubscribe from an interval Observable to avoid memory leaks and unwanted background processing. We'll see this in action in React components later in the course.

To see how interval works, let's watch a short video.

RxJS Tutorial For Beginners #9 - Interval Operator Tutorial | Angular RxJS Tutorials

This video from ARCTutorials provides a clear, hands-on demonstration of the interval operator.

Please watch from 01:08 to 09:23. The key points to focus on are: The basic concept of interval. The live coding demo showing how it emits sequential numbers. The brief but important mention of stopping the interval and the need to unsubscribe.

As you saw, interval is straightforward but powerful. Given your background in radiophysics, you can think of it as a simple, stable clock signal or pulse generator, providing a consistent temporal backbone for other operations.

2. Flexible Scheduling with timer

While interval is great for fixed-period emissions, you often need more control. This is where timer comes in. It's a more versatile function that can act as a reactive setTimeout or a more configurable setInterval.

The timer function has two main signatures:

  1. timer(dueTime): Creates an Observable that waits for dueTime milliseconds, emits a single value (0), and then completes. This is the reactive equivalent of setTimeout.

    import { timer } from 'rxjs';
    
    console.log('Subscribing now...');
    timer(2000).subscribe({
      next: val => console.log('Value emitted:', val), // Emits 0
      complete: () => console.log('Complete!')
    });
    
    // LOGS:
    // Subscribing now...
    // (after 2 seconds)
    // Value emitted: 0
    // Complete!
    
  2. timer(initialDelay, period): Creates an Observable that waits for initialDelay milliseconds, emits its first value (0), and then continues to emit subsequent values (1, 2, 3, ...) every period milliseconds.

    This is where timer distinguishes itself from interval.

    • interval(1000) is equivalent to timer(1000, 1000).
    • If you want an emission immediately and then every second, you would use timer(0, 1000). This is a very common pattern.

A Common Use Case: Polling

A great real-world example that combines timer with what we learned in the last lesson (from) is building a polling mechanism. Imagine you need to repeatedly check an API endpoint until a certain condition is met.

The following article demonstrates this pattern. It uses timer to kick off and repeat the polling, and from to convert the fetch Promise into an Observable.

Polling using RxJS - by Hendrik Wallbaum

This article, 'Polling using RxJS' by Hendrik Wallbaum, provides a practical example of how to use timer to build a polling client. It's a great illustration of combining operators.

Read the article from the beginning down to the 'Putting it all together' section. Note how timer(0, 500) is used to start polling immediately and then twice a second. The article also mentions operators like concatMap and take; we will cover those in detail in our next module. For now, focus on how timer drives the polling and from integrates the async API call.

One small correction to the article: it states that interval emits the first event immediately. As we've discussed, interval(period) actually waits for the first period to elapse before emitting 0. To get an immediate emission, timer(0, period) is the correct choice, just as the author uses in their code.

3. Controlling Lazy Execution with defer

So far, all the creation operators we've seen (of, from, interval, timer) create cold Observables. The producer logic is defined, but it only executes when a subscriber arrives.

However, a subtle issue can arise: when is the data for the Observable generated? For of(Math.random()), the random number is generated once when the Observable is defined, not when it's subscribed to. All subsequent subscribers will get the exact same random number.

What if you need to generate a fresh value or kick off a new asynchronous process for every single subscriber? This is the problem that defer solves.

defer takes one argument: a factory function that returns an Observable. This factory function is not executed until a consumer subscribes to the defer'd Observable. Each new subscription calls the factory function again, creating a fresh, new inner Observable.

This is an incredibly useful utility for ensuring truly lazy and isolated executions.

RxJS Mastery - #8 defer - Ronnie Schaniel

The article 'RxJS Mastery - #8 defer' by Ronnie Schaniel provides an excellent and concise explanation of defer with clear, practical examples.

Please read the sections 'RxJS defer operator explained' and 'What problems does the RxJS defer operator solve?'. Pay close attention to the three examples: The Math.random() example, which perfectly illustrates the core concept. The fetch (Promise) example, showing how to make an eager Promise lazy. The new Date() example, which highlights how to capture a value at the moment of subscription.

To summarize the key use cases for defer you just read about:

  1. Making Eager Sources Lazy: A Promise starts its work the moment it's created. Wrapping its creation in defer (defer(() => from(fetch(...)))) ensures the fetch is only called when someone subscribes. This is crucial for controlling when network requests are made.
  2. Capturing State at Subscription Time: When you need the most up-to-date value at the moment of subscription (e.g., new Date(), a configuration setting, the current value of a variable), defer is the tool for the job.
  3. Factory for Resources: For Observables that wrap a resource like a WebSocket or a database connection, defer can ensure that each subscriber gets their own unique connection. We'll explore this pattern in a later module.

Conclusion

In this lesson, we've added three powerful creation functions to your RxJS toolkit, moving from static data to the dimension of time and execution control.

Key Takeaways:

  • interval(period): Creates a cold Observable that emits a sequence of numbers (0, 1, 2...) at a fixed period. It runs indefinitely and must be unsubscribed from.
  • timer(delay, [period]): A more flexible time-based operator. It can act like a setTimeout (one emission after a delay) or a delayed/immediate setInterval (emissions starting after a delay and repeating at a period).
  • defer(factoryFn): A meta-operator that creates a new Observable from the provided factory function for each new subscriber. It ensures that the Observable's creation logic is executed lazily and separately for every subscription.

These operators are fundamental building blocks for many advanced reactive patterns you'll encounter and build, from simple UI animations to complex, resilient data-fetching strategies.

In our next and final lesson of this foundational module, we will learn how to implement a custom Observable using the Observable constructor. This will give you the power to wrap any asynchronous source, no matter how unconventional, into a well-behaved RxJS Observable.

Can't find a good explanation? Sign up and we'll make it for you

Sign up