My application (Angular 2 with RxJS 5 written in TypeScript) requires me to make 2 calls to a web service in a predefined order. Inspired by this page I decided to try the concat operator of RxJS observables. The twist is that the onNext callback that receives values needs to forward them to different handlers depending on where they came from
My code can be simplified to:
suscription1 = Observable.return({name:'John', age:7});
subscription2 = Observable.return({name:'Mary', color:'blue'});
Observable.concat(subscription1, subscription2).subscribe(
data=>{ /* need to know which subscripion
i'm dealing with to direct to the right handler*/}
);
What's the most elegant way to determine where my data came from at each observable emission? I have thought of a couple of ways but they all seem clumsy:
Keep a counter in scope, perhaps as a class property. Check it and increment it each time you receive a result. Since each observable emits just once, the counter will have its initial value when the first subscription outputs and it will have initial+1 when the second outputs. Problem is this won't work when I don't know how many times each observable will emit before being complete.
Inspect each result and identify its source by the result's shape. For instance, in my example only the first observable's result has an age property. Obviously this becomes impractical when the results have the same shape.
Get rid of the concat operator altogether; subscribe to subscription2 in the onComplete handler of subscription1. This would work, but it doesn't scale well; if I have 4 or 5 subscriptions to concatenate, it becomes nested hell.
Solution 3 with just 2 subscriptions...
suscription1 = Observable.return({name:'John', age:7});
subscription2 = Observable.return({name:'Mary', age:8});
subscription1.subscribe(
data=> this.handlerOne(data),
error=>{},
()=> {
subscription2.subscribe(
data=>this.handlerTwo(data),
error=>{},
()=>console.log('All subscriptions completed!')
)
}
);
So my question: When using Observable.concat to subscribe to several observables in sequence, how can my onNext handler determine where the data came from? Alternatively, is there another operator I should be using for this purpose? I cannot use the forkJoin operator because the order in which the subscriptions are completed is important.
You could mapthe suscriptions to know where it's coming from.
suscription1 = Observable.return({name:'John', age:7})
.map(person => { person.source = 1; return person };
subscription2 = Observable.return({name:'Mary', age:8})
.map(person => { person.source = 2; return person };
Then you can easily determine where it comes from:
Observable.concat(subscription1, subscription2).subscribe(
data=> { if (data.source == 1) /* do whatever */ }
);
Related
Im trying to make throttleTime take effect, but for some reason it does not kick in. I have the following:
// Class Properties
private calendarPeriodSubject: Subject<x> = new Subject<x>();
private calendarPeriodObservable$ = this.calendarPeriodSubject.asObservable();
// Throttling fails here (Inside constructor):
const calendarPeriodSubscription = this.calendarPeriodObservable$.pipe(throttleTime(750)).subscribe(async (calendar: x) => {
// Do http stuff here
}
});
The subject gets called like this:
this.calendarPeriodSubject.next(x);
I also tried with:
this.calendarPeriodSubject.pipe(throttleTime(1000)).subscribe({next: (x) => x});
I would like to process the FIRST time, and the following clicks should not have any effect before after ieg 750ms - To prevent the server from getting spammed basically.
Anyone has any idea?
Thanks!
The problem is that you are using the wrong operator for your use case. The way I understand your explanation you want to send through your first call and stop any further calls to your Server for some amount of ms. But what throttleTime(sec) does is simply put a timer on the action and execute it sec ms later. So you server will still be spammed, just a few ms later.
Your case screams debounceTime() for me. debounceTime docu
This disables any further data to be passed though the Observable for the specified time after a value has been emitted.
Therefore your code should be fine if you use something like:
const calendarPeriodSubscription =
this.calendarPeriodObservable$.pipe(debounceTime(750)).subscribe((calendar: x) => {
// Stuff with returned data
});
I'm currently using RxJS Observables / Subscription to perform HTTP requests as code similar to below demonstrates:
this.waiting = true;
this.doSomething().subscribe(
(result) => {
this.waiting = false;
this.showResult= true;
}
);
What I really want to do, is only set this.waiting to true on a predetermined length of time. In other words, you are only really 'waiting' if the Observable hasn't come back within say 30 seconds. Wondering how to achieve that. I see that there is a .timer method available, but that would only start subscribing after that length of time?
Have a look at timeout() and timeoutWith() operators. These have no documentation but from their parameters you can guess what they do.
The timeout() operator send an error notification after some time of inactivity.
The timeoutWith() I think let's you replace the source Observable with another Observable after some time of inactivity.
Eventually, if you want to avoid these two operators you can use Observable.race that subscribes internally only to the first Observable that emits:
Observable.race(Observable.timer(30 * 1000).take(1), this.doSomething())
I don't know an operator that does this but this js code should do
const source = Rx.Observable.of(1).delay(1000);
const sub = source.subscribe(val => console.log(val));
setTimeout(() => {
sub.unsubscribe();
console.log('timeout')
}, 500);
you can play around with delay and setTimeout values.
I am trying to figure out the RxJS pattern for declaring my "subscribes" such that they execute in order.
Here's the situation I'm in:
var flag = false;
// Stream that non-deterministically emits `true` when flag is true
var random$ = Rx.Observable.interval(1000)
.map(() => setFlagToRandomTrueOrFalse())
.filter(x => x)
.first();
// How can I declare my "subscribes" so that the console.logs happen in order?
var sub1 = random$.subscribe(() => console.log(1), console.log, resetFlagToFalse);
var sub2 = random$.subscribe(() => console.log(2), console.log, resetFlagToFalse);
function setFlagToRandomTrueOrFalse() {
flag = flag || !!Math.floor(Math.random() * 2);
return flag;
}
function resetFlagToFalse() { flag = false; }
This currently prints 1 and 2 in random order due to the async nature of .subscribe.
Also, what is the proper name for these "subscribes"? Total RxJS noob here.
eeerr I am not sure the order you observe is because of the async nature of subscribe.
First of all, as currently written, random$ is a cold source, i.e. a producer which has not been subscribed yet. That producer will start producing every time you subscribe to it. That means here, you are starting the producer twice, so you will have two different random values emitted for each, so the log will be shown at different times, given that you have not subscribed to the same sequence of values, even when it looks very much like so.
A more clear description of the distinction between cold and hot streams is Hot and Cold observables : are there 'hot' and 'cold' operators?. Spend as much time as necessary to understand this because this is very important before you start to do anything fancy with Rxjs.
If you want to subscribe to the same sequence of values, i.e. if you want the producer to multicast its values to all subscribers (hot behavior), instead of restarting for each subscriber (cold behavior), you need to use a variation of the multicast operator, as explained in the abovementioned link. That means in your case
var random$ = Rx.Observable.interval(1000)
.map(() => setFlagToRandomTrueOrFalse())
.filter(x => x)
.share();
Going back to your question about data propagation order, it is indeed mostly deterministic, which does not mean always simple. In a simple case as yours, the first subscribe will indeed be executed first. That is because under the hood, multicast-like operators are using subjects, which emit immediately (hot source) the value they receive to all their subscribers, in order of subscription. For information about subjects, have a look at What are the semantics of different RxJS subjects?.
Last thing, it helps a lot to reason about your streams if you compose pure functions. Having a closure variable such as flag makes thing really more complex very fast.
I recommend you to have a look here before delving much further into Rxjs : The introduction to Reactive Programming you've been missing
You can use publish() and connect() to perform the actions in sequence like this
var flag = false;
// Stream that non-deterministically emits `true` when flag is true
var random$ = Rx.Observable.interval(1000)
.map(() => setFlagToRandomTrueOrFalse())
.filter(x => x)
.first().publish();
// How can I declare my "subscribes" so that the console.logs happen in order?
var sub1 = random$.subscribe(() => console.log(1), console.log, resetFlagToFalse);
var sub2 = random$.subscribe(() => console.log(2), console.log, resetFlagToFalse);
random$.connect();
function setFlagToRandomTrueOrFalse() {
flag = flag || !!Math.floor(Math.random() * 2);
return flag;
}
function resetFlagToFalse() { flag = false; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.1.0/rx.all.js"></script>
I wanted to use rxjs for the first time but am a bit stucked 'cause it doesn't behave exactly like I want it to: In my scenario I want to create an observable from a promise. But I want the promise only being called once (not on every subscription) and I want it not being called on creation time (defer the call to the first subscription).
First I tried this:
var source = Rx.Observable.fromPromise(_this.getMyPromise())
which causes a call to the getMyPromise function right on creation time. This is not satisfying because at that time I don't know if the source really will be used.
Then I tried:
var source = Rx.Observable.defer(function() { return _this.getMyPromise() })
which causes a call to the getMyPromise function each time a new subscription is being made to source. This makes way too many unnecessary calls to the web server. The Rx.Observable.create function seems to have the same issue.
So what is left or what am I missing?
.shareReplay() does this, e.g.:
var source = Rx.Observable.defer(function() { return _this.getMyPromise() }).shareReplay();
If you're using rxjs5, you'll want to read: Pattern for shareReplay(1) in RxJS5
In answer to your comment below, I can think of a fairly straightforward extension to the above logic that will do what you want, but it has a caveat. Let's say the events you want to use to trigger a "refresh" are represented in a stream, s$, then you could do something like:
var source = Rx.Observable.of({}).concat(s$)
.flatMapLatest(function() {
return Rx.Observable.defer(function() {
return _this.getMyPromise()
})
})
.shareReplay(1)
What we have here is a stream starting with a dummy object to get things rolling, followed by a stream consisting of your refresh events. Each of these is projected into a new observable created from a fresh invocation of your getMyPromise method, and the whole thing is flattened into a single stream. Finally, we keep the shareReplay logic so we only actually make calls when we should.
The caveat is that this will only work properly if there's always at least one subscriber to the source (the first subscription after all others are disposed will run the promise again, and will receive both the previously-cached value and the result of the promise it caused to run).
Here is an answer that does not require at least one subscriber at the source at all times using a simple helper:
var _p = null;
var once = function() { return _p || (_p = _this.getMyPromise());
var source = Rx.Observable.defer(once);
Or if you're using lodash, you can _.memoize your getMyPromise and get this automatically.
Let's say that I want to pass a Scheduler to an RxJS operator that makes it emit notifications every 5 seconds. Of course, this is very easy to do by just using interval or other existing operators. But if I really want to use a scheduler to accomplish that, how would I go about it?
My first thought is to subclass Rx.Scheduler.default. Would that be the way to go? And if so, how could that subclass look? Again, I understand that this is a complicated way to accomplish something that's easy using operators, but I am just curious about custom schedulers.
Operations should always be independent of the Schedulers that are used to implement them. Schedulers only know about one thing, time. Every scheduler is specifically built to deal with its own notion of time. They are expressly not built to handle specific operators since that would be a conflation of concerns.
So for your stated goal of creating a recurring task, I wouldn't recommend trying to actually create your own scheduler, it simply isn't needed. Schedulers come with an interface that already supports this.
You can use either the schedulePeriodic or the scheduleRecursiveFuture to accomplish this.
//Using periodic
Rx.Observable.interval = function(period, scheduler) {
return Rx.Observable.create(function(observer) {
return scheduler.schedulePeriodic(0, period, function(count) {
observer.onNext(count);
return count + 1;
});
});
};
//Using scheduleRecursive
Rx.Observable.interval = function(period, scheduler) {
return Rx.Observable.create(function(observer) {
return scheduler.scheduleRecursiveFuture(0, period, function(count, self) {
observer.onNext(count);
self(period, count + 1);
});
});
};
Reference 1,
Reference 2;
The former should be easier to wrap your head around, essentially it is just scheduling something to occur repeatedly spaced in time based on the period parameter.
The latter is usually a little more difficult to explain, but essentially you are scheduling a task and then sometime during the execution of that task you are rescheduling it (which is what the self parameter) is doing. This allows you do get the same effect using the period parameter.
The timing of this work is all directly affected by which scheduler you decide to pass into the operator. For instance, if you pass in the default it will try to use the best method for an asynchronous completion, whether that be setTimeout, setInterval or some other thing I can't remember. If you pass in a TestScheduler or a HistoricalScheduler this actually won't do anything until you increment each of their respective clocks, but doing so gives fine grained control over how time flows.
tl;dr Only implement new Schedulers if you have some new overall notion of time to express, otherwise use the existing API to do work on whatever Scheduler best fits how you want time to pass.
Should you roll your own?
Plainly: No. Most likely you can get done what you need done with an existing operator. Something like buffer, window, sample, etc. Scheduler development is not completely straightforward.
How to roll your own RxJS 4 Scheduler
If you want to implement your own Scheduler, in RxJS 4, you'd subclass Rx.Scheduler, then override each schedule method: schedule, scheduleFuture, schedulePeriodic, scheduleRecursive, scheduleRecursiveFuture... You'd also likely want to override now to return something relevant to your schedule.
Here is an example of a custom scheduler that uses button clicks inside of real time
/**
NOTE: This is REALLY fast example. There is a lot that goes into implementing a
Scheduler in RxJS, for example what would `now()` do in the scheduler below? It's also missing a number of scheduling methods.
*/
class ButtonScheduler extends Rx.Scheduler {
/**
#param {string} the selector for the button (ex "#myButton")
*/
constructor(selector) {
super();
this.button = document.querySelector(selector);
}
schedule(state, action) {
const handler = (e) => {
action(state);
};
const button = this.button;
// next click the action will fire
button.addEventListener('click', handler);
return {
dispose() {
// ... unless you dispose of it
button.removeEventListener('click', handler);
}
};
}
// Observable.interval uses schedulePeriodic
schedulePeriodic(state, interval, action) {
const button = this.button;
let i = 0;
const handler = (e) => {
const count = i++;
if(count > 0 && count % interval === 0) {
state = action(state);
}
};
// next click the action will fire
button.addEventListener('click', handler);
return {
dispose() {
// ... unless you dispose of it
button.removeEventListener('click', handler);
}
};
}
}
Rx.Observable.interval(1, new ButtonScheduler('#go'))
.subscribe(x => {
const output = document.querySelector('#output');
output.innerText += x + '\n';
});
How to do it in RxJS 5 (alpha)
Scheduling changed again in RxJS 5, since that version was rewritten from the ground up.
In RxJS5, you can create any object that adheres to the following interface:
interface Scheduler {
now(): number
schedule(action: function, delay: number = 0, state?: any): Subscription
}
Where Subscription is just any object with an unsubscribe function (same as dispose, really)
Once again, though, I don't advise creating a scheduler unless it's completely necessary.
I really hope that helps answer your question.