RxJS - Fire subscribes in order - javascript

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>

Related

How to make sure that a method is called only once given specific parameters

i have a class called Feature and it contains the following methods setUser(boolean),execute(), doExecute()
And according to the below stated parameters, when i call execute() method, doExecute() method should be called only once.
I tried to test that doExecute() method is called only once in the below code using sinon, but I receive an error message says: doExecute() method is called zero times.
please let me know how to check correctly if doExecute() is called exactly once
code:
t.context.clock = sinon.useFakeTimers();
const domain = 'testDomain';
const delayInMillis = 0;
const delayInSecs = delayInMillis / 1000;
const feature = new Feature(domain, delayInMillis);
feature.setUser(false);
const p = feature.execute()
.then(() => sinon.spy(feature.doExecute()))
.then(() => t.pass());
sinon.assert.callCount(sinon.spy(feature.doExecute()),1);
t.context.clock.restore();
return p;
});
First of all, sinon.spy() accepts a function as a parameter, and you're passing the result of your function.
// wrong
sinon.spy(feature.doExecute());
// correct
sinon.spy(feature.doExecute);
Next, you need to store your spy into a variable for later reference. In your code you're just creating new spies every time.
const featureDoExecuteSpy = sinon.spy(feature.doExecute);
// some code
sinon.assert.callCount(featureDoExecuteSpy, 1);
There are quite a few problems with this.
As sameeh pointed out, you're simply creating a new spy as part of your assertion. Obviously a new spy will have a call count of zero. You need to make sure the assertion is referring to the same spy instance as will be called by the code under test.
A crucial thing that sameeh missed, however, is that the spy has to be passed through to your code under test. This is why the usual form of `sinon.spy()' accepts an object followed by a method name. It replaces the method on that object with a spy that wraps the original:
// wrong
sinon.spy(feature.doExecute());
// still wrong
sinon.spy(feature.doExecute);
// correct
sinon.spy(feature, 'doExecute');
You can then access the spy in place on that object for your assertion. You don't need to store it in a local variable:
sinon.assert.callCount(feature.doExecute, 1);
Another problem: Your assertion is not waiting for the promise from feature.execute to resolve. This means that if doExecute is being invoked after some asynchronous operation within execute, your assertion is happening too early. It thus needs to be in a then after the others like so (note that this code still won't work due to other problems I'll address in a bit):
const p = feature.execute()
.then(() => sinon.spy(feature, 'doExecute'))
.then(() => t.pass())
.then(() => sinon.assert.callCount(feature.doExecute,1));
More problems... Your use of fake timers is very strange, and makes it difficult for me to tell whether or not the promise returned by feature.execute() will ever resolve.
If it needs the timer to tick in order to resolve? It won't. Because you aren't ever ticking the timer. I don't know what t.pass() does, but since it is chained onto the promise returned by feature.execute(), it will never get called if you don't tick the timer somewhere else. Nor will your code that creates your spy, for that matter-- for the same reason.
You need to create your spy before invoking feature.execute(), and probably invoke t.pass() after, if it is indeed the method that ticks your fake timer:
sinon.spy(feature, 'doExecute')
const p = feature.execute()
.then(() => sinon.assert.callCount(feature.doExecute,1));
t.pass();
Finally, I don't know what test framework you're using, but you generally want to restore fake timers and other global state in blocks that will always execute, whether or not your test is successful. This ensures that failed tests don't leave crap hanging around that will carry over into other tests. Mocha has methods beforeEach and afterEach for this purpose:
beforeEach(function() {
t.context.clock = sinon.useFakeTimers();
});
afterEach(function() {
t.context.clock.restore()
});
it('whatever your test name is', function() {
const domain = 'testDomain';
const delayInMillis = 0;
const delayInSecs = delayInMillis / 1000;
const feature = new Feature(domain, delayInMillis);
sinon.spy(feature, 'doExecute')
const p = feature.execute()
.then(() => sinon.assert.callCount(feature.doExecute,1));
t.pass();
return p;
});

RxJS detect long running subscription

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.

RxJS Observable.concat: How to know where next result came from?

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 */ }
);

Passing a non-standard Scheduler to an operator

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.

How can I turn an observable into an observable of long polling observables which complete on a specific value?

I'm creating an interactive webpage with RxJs.
This is what I want to achieve:
I have an application that generates tokens. These tokens can be consumed by an external entity.
When a user creates a token, the page starts polling the webserver for its status (consumed or not). When the token is consumed, the page refreshes.
So, when the token is created, a request is sent to the server every 2 seconds asking whether the token is consumed yet.
I have an Observable of strings that represent my generatedTokens.
I actually already have a working implementation using the Rx.Scheduler.default class, which allows me to do things manually. However, I can't help but feel that there should be a much simpler, more elegant solution to this.
This is the current code:
class TokenStore {
constructor(tokenService, scheduler) {
// actual implementation omitted for clarity
this.generatedTokens = Rx.Observable.just(["token1", "token2"]);
this.consumedTokens = this.generatedTokens
.flatMap(token =>
Rx.Observable.create(function(observer) {
var notify = function() {
observer.onNext(token);
observer.onCompleted();
};
var poll = function() {
scheduler.scheduleWithRelative(2000, function() {
// tokenService.isTokenConsumed returns a promise that resolves with a boolean
tokenService.isTokenConsumed(token)
.then(isConsumed => isConsumed ? notify() : poll());
}
);
};
poll();
}));
}
}
Is there something like a "repeatUntil" method? I'm looking for an implementation that does the same thing as the code above, but looks more like this:
class TokenStore {
constructor(tokenService, scheduler) {
// actual implementation omitted for clarity
this.generatedTokens = Rx.Observable.just(["token1", "token2"]);
this.consumedTokens = this.generatedTokens
.flatMap(token =>
Rx.Observable.fromPromise(tokenService.isTokenConsumed(token))
.delay(2000, scheduler)
// is this possible?
.repeatUntil(isConsumed => isConsumed === true));
}
}
Funnily enough the answer struck me a few minutes after posting the question. I suppose rubberducking might not be so silly after all.
Anyway, the answer consisted of two parts:
repeatUntil can be achieved with a combination of repeat(), filter() and first()
fromPromise has some internal lazy cache mechanism which causes subsequent subscriptions to NOT fire a new AJAX request. Therefore I had to resort back to using Rx.Observable.create
The solution:
class TokenStore {
constructor(tokenService, scheduler) {
// actual implementation omitted for clarity
this.generatedTokens = Rx.Observable.just(["token1", "token2"]);
this.consumedTokens = this.generatedTokens
.flatMap(token =>
// must use defer otherwise it doesnt retrigger call upon subscription
Rx.Observable
.defer(() => tokenService.isTokenConsumed(token))
.delay(2000, scheduler)
.repeat()
.filter(isConsumed => isConsumed === true)
.first())
.share();
}
}
A minor sidenote: the "share()" ensures that both observables are hot, which avoids the scenario where every subscriber would cause ajax request to start firing.
class TokenSource {
constructor(tokenService, scheduler) {
this.generatedTokens = Rx.Observable.just(["token1", "token2"]).share();
this.consumedTokens = this.generatedTokens
.flatMap(token =>
Rx.Observable.interval(2000, scheduler)
.flatMap(Rx.Observable.defer(() =>
tokenService.isTokenConsumed(token)))
.first(isConsumed => isConsumed === true))
.share()
}
}
You can take advantage of two facts:
flatMap has an overload that takes an observable which will be resubscribed to every time a new event comes in
defer can take a method returning a promise. The method will be re-executed every subscription, which means you do not have to roll your own Promise->Observable conversion.

Categories

Resources