Introduction to the Reactive Extensions for JavaScript – The Final Countdown Timer

In the previous couple of posts, I’ve talked about asynchronous method chaining and creating custom schedulers so I can repeatedly hit a data source for its data, transform it and then display the results.  Instead of just setting a custom interval between the results and querying the system, so this time, I want to stick a timer where I can monitor the time between queries. 

Before we get started, let’s get caught up to where we are today:

Creating Countdowns with GenerateWithTime

In previous posts, the goal was to repeatedly query a system with a given interval so that we could see what is going on in the world of Twitter.  The goal of this post is to take this idea a step further to have a timer display how long we have until the system refreshes itself, and when the timer is exhausted, we requery and reset the timer.  To do this, we’ll need to make use of the GenerateWithTime function.  This function takes five parameters:

  1. The initial state of the timer
  2. The condition of the timer of whether to continue
  3. The result selector which takes the state and you return what you want from it
  4. The time selector which says how much time you want between calls
  5. The iterate function which takes the state and increments it

Below is the signature of the GenerateWithTime function:

// initialState : State
// condition : State -> bool
// resultSelector : State -> Result
// timeSelector : State -> int
// iterate : State -> State

Rx.Observable.GenerateWithTime = function(
    initialState, 
    condition, 
    resultSelector, 
    timeSelector, 
    iterate);

Now that we have some clue on what it’s expecting, let’s go ahead and put it to some good use.  What we’re going to do is create a timer that counts down from 90 seconds while setting the countdown element to the current value at each second interval.  You’ll notice the initial state is set to 90, the condition is while the state is greater than or equal to zero, our time selector is one second and our iterate decrements the state by one.

function countdownUI() {
    return Rx.Observable.GenerateWithTime(
        90,
        function(x) { return x >= 0; },
        function(x) { return x; },
        function(x) { return 1000; },
        function(x) { return x - 1; })
        .Do(function(x) { $("#countdown").html(x); });
}

Our ultimate goal is to have something happen at the front end, such as query Twitter, count down from 90 seconds and then query again.  To facilitate that need, I’ll create the updateUI function which queries Twitter and pulls the first record that matches and then displays the result in our content area.

function updateUI() {
    var url = "http://search.twitter.com/search.json?q=4sq.com&rpp=1";
    return $.getJSONAsObservable(url)
        .Select(function(d) { return d.data.results[0].text; })
        .Do(
            function(result) {
                $("#content").html(result);
            });
}

Once we’ve defined both our countdownUI and updateUI functions, let’s compose them together in such a way that it fires the updateUI, counts down to zero, then rinse and repeat.  To make use of that, we must first defer the Twitter search until we’ve subscribed which allows us to repeatedly call it and have it requery.  The Defer function is a static function which takes a function which takes no arguments and you must return an observable sequence.

// observableFactory : () -> Observable

Rx.Observable.Defer = function(
	observableFactory);

Then once we have deferred our UI update, then we need to add it on to our existing observable using Concat so that we count down to zero before repeating.

Rx.Observable.prototype.Concat = function (
	o1);

And once we Concat, we repeat it all over again to keep querying and counting down.  We can now run our observable sequence by calling Subscribe with no arguments (since we’re not doing anything with the results).

$(document).ready(function() {
    Rx.Observable.Defer(function() { return updateUI(); })
        .Concat(countdownUI())
        .Repeat()
        .Subscribe();
});

Putting the code all together, we’re able to watch this in action where it queries Twitter for mentions of 4sq.com and displays the first result every 90 seconds.

image 

You can find the entire code for this example here.

Side Note

Just as a side note, many of these scenarios that I’ve been doing for this series apply to the Reactive Extensions for .NET as well.  For example, we could do something similar in C# that we had above.  For example, we could have our two services, each of which return nothing (Unit in this case), but do their actions as a side effect instead.

IObservable<Unit> UpdateResults() 
{
    return SearchTwitter()
        .Select(data => data.results[0])
        .ObserveOnDispatcher()
        .Do(result => Item.Result = result)
        .Select(_ => new Unit());
}

IObservable<Unit> Countdown()
{
    return Observable.GenerateWithTime(
            90,
            time => time >= 0,
            time => time,
            _ => TimeSpan.FromSeconds(1),
            time => time - 1)
        .ObserveOnDispatcher()
        .Do(time => Item.Refresh = TimeSpan.FromSeconds(time))
        .Select(_ => new Unit());
}

And then we can listen much like before with our JavaScript example with complete parity between the two examples.

Observable.Defer(() => UpdateResults)
    .Concat(Countdown())
    .Repeat()
    .Subscribe();

The difference between the two is quite small, except for the observing on the dispatcher as well as making sure that our observable sequences have the same signature of IObservable<Unit>.  In the JavaScript land, we don’t have to care since we’re dealing with a weakly typed dynamic language.  Hopefully that’ll give you a bridge between the two worlds.  In the future, I’ll make sure how to bridge the gap between the two worlds more often.

Conclusion

Dealing with asynchronous programming has been in the forefront of many minds in the JavaScript community.  At JSConf, there were several examples of frameworks trying to get around the idea of callbacks and instead lean more towards composition.  By utilizing the Reactive Extensions for JavaScript, we’re able to compose together asynchronous and event-based operations together and transform the results in interesting ways.  One such example is querying a system and having a public countdown until the next refresh is going to be done.  That’s just one of the many things you can do with RxJS.

So with that, download it, and give the team feedback!

No Comments