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.
Related
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.
Earlier I ran into the issue of Alexa not changing the state back to the blank state, and found out that there is a bug in doing that. To avoid this issue altogether, I decided that I wanted to force my skill to always begin with START_MODE.
I used this as my reference, where they set the state of the skill by doing alexa.state = constants.states.START before alexa.execute() at Line 55. However, when I do the same in my code, it does not work.
Below is what my skill currently looks like:
exports.newSessionHandler = {
LaunchRequest () {
this.hander.state = states.START;
// Do something
}
};
exports.stateHandler = Alexa.CreateStateHandler(states.START, {
LaunchRequest () {
this.emit("LaunchRequest");
},
IntentA () {
// Do something
},
Unhandled () {
// Do something
}
});
I'm using Bespoken-tools to test this skill with Mocha, and when I directly feed IntentA like so:
alexa.intended("IntentA", {}, function (err, p) { /*...*/ })
The test complains, Error: No 'Unhandled' function defined for event: Unhandled. From what I gather, this can only mean that the skill, at launch, is in the blank state (because I have not defined any Unhandled for that state), which must mean that alexa.state isn't really a thing. But then that makes me wonder how they made it work in the example code above.
I guess a workaround to this would be to create an alias for every intent that I expect to have in the START_MODE, by doing:
IntentA () {
this.handler.state = states.START;
this.emitWithState("IntentA");
}
But I want to know if there is a way to force my skill to start in a specific state because that looks like a much, much better solution in my eyes.
The problem is that when you get a LaunchRequest, there is no state, as you discovered. If you look at the official Alexa examples, you will see that they solve this by doing what you said, making an 'alias' intent for all of their intents and just using them to change the state and then call themselves using 'emitWithState'.
This is likely the best way to handle it, as it gives you the most control over what state and intent is called.
Another option, assuming you want EVERY new session to start with the same state, is to leverage the 'NewSession' event. this event is triggered before a launch request, and all new sessions are funneled through it. your code will look somewhat like this:
NewSession () {
if(this.event.request.type === Events.LAUNCH_REQUEST) {
this.emit('LaunchRequest');
} else if (this.event.request.type === "IntentRequest") {
this.handler.state = states.START;
this.emitWithState(this.event.request.intent.name);
}
};
A full example of this can be seen here (check out the Handlers.js file): https://github.com/alexa/skill-sample-node-device-address-api/tree/master/src
I would also recommend reading through this section on the Alexa GitHub: https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs#making-skill-state-management-simpler
EDIT:
I took a second look at the reference you provided, and it looks like they are setting the state outside of an alexa handler. So, assuming you wanted to mimic what they are doing, you would not set the state in your Intent handler, but rather the Lambda handler itself (where you create the alexa object).
exports.handler = function (event, context, callback) {
var alexa = Alexa.handler(event, context);
alexa.appId = appId;
alexa.registerHandlers(
handlers,
stateHandlers,
);
alexa.state = START_MODE;
alexa.execute();
};
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 */ }
);
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.
Setting:
let's say three events happen in three separate part of some application, the event should be handled by two controllers. The application dispatcher is responsible for sending/receiving events from all parts of the application, and this dispatcher should have an asynchronous event queue.
In some cases, two the of three events are related along some attribute but are not of the same name, and only one should be passed to the controller, the other one may be discarded.
Problem:
Currently, I have a 'queue' that really just bounces the event to the controller, this is unhelpful because I have no way of comparing two events in the queue if only one is ever there at time.
So how do I ensure the events should stay in the queue for a while? I suppose a timeout function could do the trick but is there a better way?
To give credit where it's due, the idea of coalescing events is inspired by Cocoa and I'm basically trying to do something similar.
I don't know much about Cocoa, but I assume that it replaces older events with the latest event until the event is able to be dispatched to the application (i.e. if the application is busy for some reason). I'm not sure what your particular use case is, but if you want to rate-limit the events, I would use setTimeout like this:
function Dispatcher(controllers) {
this.controllers = controllers;
this.events = [];
this.nextController = 0;
}
Dispatcher.prototype = {
_dispatch: function (i) {
var ev = this.events.splice(i, 1);
this.controllers[this.nextController].handle(ev);
this.nextController = (this.nextController + 1) % this.controllers.length;
},
notify: function (ev) {
var index = -1, self = this, replace;
function similer(e, i) {
if (e.type === ev.type) {
index = i;
return true;
}
}
replace = this.events.some(similar);
if (replace) {
this.events[i] = ev;
} else {
// it's unique
index = this.events.push(ev) - 1;
setTimeout(function () {
self._dispatch(index);
}, 100);
}
}
};
Just call notify with the event (make sure there's a type property or similar) and it will handle the magic. Different types of events will be handled uniquely with their own setTimeout.
I haven't tested this code, so there are probably bugs.
I suppose a timeout function could do the trick but is there a better way?
No, there really isn't.
Usually the way to go is using setTimeout(..., 0) if your events are dispatched in the same run loop. So the implementation would look something like this:
var eventQueue = [];
var handlerTimer = -1;
function fireEvent(event, params) {
eventQueue.push([event, params]);
window.clearTimeout(handlerTimer);
handlerTimer = window.setTimeout(resolveQueue, 0);
}
function resolveQueue() {
// process eventQueue and remove unwanted events...
// then dispatch remaining events
eventQueue = [];
}
If you need to handle events from different run loops (for example native events like mouseup and click), you need to use some timeout value other than 0. The exact value depends on how long you want to accumulate events.