I’m still learning this topic, and I may refine my understanding more as I go, but this post will be helpful at least to me as a reminder of something I understood better today.
The hardest thing for me to get my head around so far on the subject of RxJS is how exactly we create and use an Observable that is not a Subject. Subjects make sense to me. Basically it just follows the pub-sub pattern. You can pass something a Subject, and then it can subscribe and respond in some way to messages posted from the Subject. And you can actually control what messages get posted using a subject#next method. Using a generic Observable, I could not figure out how you create on that could be useful at all. Making an Observable from a static array of numbers is something you see in the examples, and that is not at all useful.
I’m not going to go into a long, textbooky discussion. Here’s just a quick example of something I did. It probably isn’t even the best way to accomplish my goal, but it does illustrate some things I didn’t understand before.
I wrote a little wrapper around the $http service’s get method in AngularJS.
this._get = function(url) {
let iteration = 0;
let observable = new Observable((observer)=> {
let attempt = function() {
if (iteration >= numTries) {
observer.next({
status: "FAIL"
});
} else {
observer.next({
status: "ATTEMPTING",
data : {
attemptNum : iteration
}
});
iteration++;
$http.get(url)
.then((response)=>{
if (response.status == 200) {
observer.next({
status: "SUCCESS",
data : response.data
})
} else {
attempt();
}
})
.catch((e) =>{
attempt();
})
}
}
attempt();
})
return observable;
}
To create the observable function here, I defined the function that gets executed when someone subscribes to it. In short, what this does is attempt the $http.get several times, and it pushes updates on progress to the subscriber. As it turns out, inside this function, which gets passed into the constructor, you can use a next method to post messages to the subscriber—which is something you can’t do outside the constructor. I understand why now.
At first I did try to do this with a Subject because that was the only kind of Observable I knew how to work with. I first created the Subject at the top, and then I wrote some code very similar to what you see here, except that I had calls to subject.next, and this was not passed into the constructor of the Subject. That didn’t work, and finally I realized why.
Basically, by the time I had subscribed to the subject, I had already missed the first “ATTEMPT” message. With a subject, the execution does not wait until there is a subscriber. It broadcasts its messages to whomever has subscribed, and the subscriber only gets messages that are pushed from the Subject after it subscribes. But what I wanted was for this function to be executed only when I had actually subscribed to it. There was the important distinction I had been missing earlier, the reason why I didn’t understand to value of Observables and how to make one and how it posts values.
This also clarifies why you can’t execute something like a next method on an Observable outside its subscribe function. Basically, each subscriber gets its own copy of the Observable object and the stream of data it produces, in order, from the beginning. It doesn’t make any sense to execute next on an Observable that isn’t a Subject because that would mean sending all subscribers some message out of order whereas each subscriber to the Observable has its own stream, in order, instantiated as soon as he subscribes and not before.
Now, I am aware at least vaguely that there are built-in methods for bridging between promises and Observables, so there was probably a better way to accomplish what I did here. The point though is that you can do this and it works, and here we have a very basic example of constructing an Observable, and I could use this pattern in many situations where I want to do some kind of asynchronous thing and post updates about progress to the subscriber.