Creating a setTimeout Observable
Check out a free preview of the full Asynchronous Programming in JavaScript (with Rx.js Observables) course
The "Creating a setTimeout Observable" Lesson is part of the full, Asynchronous Programming in JavaScript (with Rx.js Observables) course featured in this preview video. Here's what you'd learn in this lesson:
To further demonstrate how Observables operate, Jafar converts the JavaScript setTimeout function into an Observable. He then compares this implementation to the Observable.fromEvent code in the Reactive Ex library.
Transcript from the "Creating a setTimeout Observable" Lesson
[00:00:00]
>> [MUSIC]
[00:00:03]
>> Jafar Husain: Let's do you setTime out as an observable. I'm just gonna create my handle and call. Actually, I'm just going to take this code right here, paste it in. However, instead of just calling some arbitrary handler, inside of here, we're just going to call observer on next. Now, I'm not really going to get, I'm just basically going to give it undefined.
[00:00:24]
We don't care about the value, right? SetTimeout doesn't really emit a value. So I'm just gonna call next to send a message essentially that the timeout has elapsed to the observable. And now, because setTimeout's never gonna fire again after this right. SetTimeout fires only once after a certain elapse time, so I also wanna say you know what?
[00:00:45]
There's not gonna be any more callbacks and so I tell the observer, onCompleted.
>> Jafar Husain: So that's what observable is I'm not quite done but it's pretty close. So now if I take this time out,
>> Jafar Husain: I'm actually gonna create a
>> Jafar Husain: Timeout function which when given the time, will return an observable.
[00:01:26]
>> Jafar Husain: Which fires after that time.
>> Jafar Husain: Now I can just call timeout, five seconds and now what I've done is I created an observable that will fire after five seconds once you for each over it. But notice just this code all I've done is create an object with a function.
[00:01:51]
Hasn't done a darn thing. Yes?
>> Speaker 2: Do we have an observer in scope? Like where are we getting the value for our observer variable?
>> Jafar Husain: We're not. I think sorry going to expand this out a little bit. We're not because observer is that for each is just a function waiting to be called.
[00:02:09]
It's when we call forEach that we will actually pass in that observer object. Does that makes sense? So time out is just going to create us this observable right here. Notice we haven't written, we haven't run the thing, right? Just created an object with a function on it, haven't done anything.
[00:02:27]
But as soon as I do this,
>> Jafar Husain: Now, I'm traversing the observable. I've called forEach. And now we can see that why nothing happens until we call forEach. It's only when the forEach function gets called that we're actually going to call a set time out on the browser.
[00:02:51]
Does that makes sense? Now, we're missing a piece. forEach always returns a subscription object. Remember that, that subscription object? Because we need a way for the consumer of the data to say I don't want that anymore. I don't want the result of timeout. We need a way for them to unsubscribe so to speak, right?
[00:03:10]
And a subscription object, much like an observable, is just an object with a single function on it called dispose. Anybody wanna tell me how to complete this function? So this is how you unsubscribe from a setTimeout, right? So when somebody calls dispose on the subscription object,
>> Jafar Husain: I want to make sure to never call, ever call, the onNext, onError, onCompleted functions ever again.
[00:03:48]
How do I make sure that never happens?
>> Speaker 3: onNext empty.
>> Jafar Husain: So I could effectively set the onNext method to an empty function. Right? But that would work, right? But now I'm actually changing somebody else's object, remember? Somebody passed this object to me. I don't want to muck with somebody else's object.
[00:04:13]
Now, I told you earlier, there's an API that will cause a timeout to stop basically. You can cancel a timeout using this clear timeout API right here. So all I have to do is when somebody calls subscription.dispose, call this. That's how an observable cleans up after itself. It's the observable's responsibility, such that when you say, look, I don't want the data anymore, for it to clean up after itself.
[00:04:42]
So now, by doing clear Timeout handle, what we've actually done is, the browser has now freed this handler. Because that's what happens when you call clear timeout. Because you're basically saying to the browser, don't call me. So then the browser naturally frees your call back cause why would it hold on your callback if it's never gonna call you?
[00:05:00]
So we've cleaned up after ourselves. This is what's different about creating a promise and creating observable. There's this extra piece here at the end which is you have to describe how if somebody decides they want to get that data any more, how to clean up after yourself. It's just a slight change to this to make this work for dominance.
[00:05:17]
So let's actually write from event.
>> Jafar Husain: So, once again we see the same pattern first, as soon as forEach is called, we wanna subscribe up to the event. We wanna cause whatever side effect is gonna happen, that is gonna lead to us eventually getting push that value. So, in this case I'm gonna go dom.addEventListener, eventName and I'm going to pass this handler directly to it.
[00:05:47]
Still you know just a slightly different shape of an API but basically doing the same thing. This time, however, setTimeout doesn't really give you an interesting value which is sort of calls you and there's nothing interesting past to the function. However, here, events obviously do give you an interesting value.
[00:06:01]
So I want to thread along the event object that you get in addEventListener into the stream. So the observable just becomes a stream of all of the event objects that your handler would have been called with had you used addEventListener. And now, because an event doesn't end after the first item, right, there could be many, many more of such items.
[00:06:21]
I'm not going to call on completed because the stream isn't completed, right. The event could call you a hundred more times, there could be a hundred more mouse moves, right. So I'm not going to say it's complete because it isn't. So, now we're actually adopting a Dom event into an observable.
[00:06:35]
But what's the missing piece?
>> Speaker 4: removeEventListener.
>> Jafar Husain: Right, we have to clean up after ourselves. So cleaning up after ourselves with a setTimeout means calling clearTimeout. But the cleaning are up after ourselves with an event means calling removeEventListener.
>> Jafar Husain: Oops, now I have to give my handler a name.
[00:06:57]
This is why dealing with events is so annoying as far as I'm concerned. It's a classic example of why it's so annoying. You have to hold on to the handler, this is the sort of stuff that we don't have to do, thanks to takeUntil, right? Instead of having to hold on to some handler and then later on calling removeEventListener, I'm actually just going to use takeUntil to declaratively describe when I want an observable stream to end.
[00:07:18]
Turns out to be much less code. So now that I've given this thing a name,
>> Jafar Husain: Now I'm gonna pass that same handler. And that's how we adapt a dom event into an observable. So that's what's going on under the hood. Hopefully you guys have got a good enough sense of now how an observable works.
[00:07:38]
Just an object with a function waiting to be called, right. And then at that point, it will hook up to an event and return a subscription which, when disposed of, will do whatever it needs to, to make sure that you're never called it again and clean up after itself.
[00:07:50]
So let's go back to our exercises.
>> Speaker 5: They're asking if you could throw a new gist for them.
>> Jafar Husain: That's a good question.
>> Speaker 5: Just to save the example.
>> Speaker 5: And in your set time out example, someone was saying wouldn't set interval be more appropriate?
>> Jafar Husain: Well appropriate, yes in the sense that a set interval is just like a setTimeout except it actually calls your call back every two hundred milliseconds.
[00:08:17]
But both are equally appropriate. So the only difference between the setTimeout example we saw in the set interval example is that we wouldn't call on completed. Because the set interval calls you again and again and again every two seconds. A setTimeout just calls you after two seconds. But the answer is that they're both totally appropriate to convert into an observable.
[00:08:33]
But the message here is take every single asynchronous API in your system and adapt it into an observable. Because then you can combine together these asynchronous data sources seamlessly. So, yeah, adapt the setTimeout into an observable. Have it call you once and then call you on completed. And yes, adapt set interval into an observable and have it just keep calling you on next forever, right?
[00:08:52]
So if I take that same example, let me just see if I can throw in a gist here.
>> Jafar Husain: So now if you take that and simply run it, it won't work, and the reason is that the observable is actually a constructor with a prototype. So it has map and filter and all those other chains on it.
[00:09:11]
I'm just showing you the actual, what the object really looks like structurally. Now if you want to make this really work with Rx, it just has a helper function for creating observables called Observable.create. And instead of creating an object with a forEach method, you just directly pass in the only method on an observable, which is forEach, so it's kind of a handy little helper function.
[00:09:35]
And this will actually work in Rx. And the other thing that the Observable.create does, so instead of forcing you to create an object with only one method, it's just a helper function you can pass in just the definition of forEach. The same thing happens, instead of forcing you to create a subscription object with only one method that's a function, it allows you to just directly return the Dispose function, and then it wraps it inside of a subscription object.
[00:09:59]
So those you out there trying it, this will actually work. Yeah, question?
>> Speaker 5: They're saying that they get that onCompleted would never be called on a dom event. But when would onError get called? Is it always up to the specific API?
>> Jafar Husain: Well for a dom event, for your typical dom event, never because a mouse move if never going to tell you error, right?
[00:10:21]
But there are some events that look like dom events, like on an XMLHttpRequest, for example, right? Where although the XMLHttpRequest doesn't have a specific API for giving you an error, well, my point here is that different push data sources may have different arbitrary ways of communicating errors. And if you wanna adapt that data source, that push data source into an observable, you need to just sort of check for whatever their particular way of communicating an error is.
[00:10:48]
And then if you get that, call onError. So that's your job, to take whatever the specific semantics are of whatever that push data source are, and convert it into those three well defined semantics on an observable. So if I was in an HTML request, for example, I believe I would check to see if I'm trying to recall, but I believe that I would check to see if the state, if the return value was a 500.
[00:11:07]
And so if I got to 500 on the the response, I would call onError and then passing that response. That might be one potential way of mapping an xmlhttp request into an observable data source. Does that makes sense?. So the problem we have today is we have all these different APIs and they all have different, just like you saw with setTimeout and addEventListener for no particularly good reason, they all have different APIs for doing basically the same thing.
[00:11:32]
And what observable can do is it can give you a single common API for all these data sources. So it's well worth your time to take whatever weird API you happen to be looking at, adapt it into an observable so that you can begin to use the operators, those powerful operators, the map, filter, take and till to glue them all together.
[00:11:48]
So, that's the first step, take whatever API you have, adapt it into an observable.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops