rxjs using promise only once on subscribe - javascript

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.

Related

Angular 2, throttleTime on observable from subject

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

Firebase functions - adding new value to the oldvalue

I'm creating an android app that logs how long a person spends on certain things. I want to add the time spent to the total time spend, so I know how long a user has spent on an exercise type
I want to do it in a function, since I think it's easier than transactions.
exports.addExerciseTime = functions.database.ref('users/{userid}/stats/exerciseTime/{type}').onWrite( event =>{
console.log("Exercise time updates...");
var newdata = event.data.val();
var oldData = event.data.previous.val();
return event.data.ref.update(oldData+ newdata);
});
Now, I know that this function will loop until firebase shuts it down.
But how would I do this? Is there an easier way to do this?
you have an easy option of adding a flag indicating that you updated the data. next time you will get into the function, just start by checking if the flag exists in if so, exit the function. the con of this one is that you will run the function at least n+1
another option, according to their latest post, you know have a "onUpdate" and "onCreate" triggers as well. you might be able to use them smartly to optimize this even more (for example: only on first creation do XYZ, so it won't run on each update).
https://firebase.googleblog.com/2017/07/cloud-functions-realtime-database.html
Like you are saying, onWrite will capture every writing event. My solution would be replacing onWrite with onCreate, however let the user write to another path because Firebase will keep triggering the function. Besides that, your approach this is not the best solution since the updates can conflict. The use of transactions is better. That would look like this:
exports.addExerciseTime = functions.database.ref('users/{userid}/stats/exerciseTime/{type}').onCreate( event =>{
console.log("Exercise time updates...");
var newdata = event.data.val();
const pathToValue = //create the path here to exercisetime
return pathToValue.transaction(function(exercisetime) {
return (exercisetime || 0) + newdata;
});
});
*Notice the onCreate event instead of onWrite. Again: You will need to write it to a other path.

Why should we use Observer/Pub-Sub pattern here?

I am trying to learn Observer and publisher-subscriber pattern.
came through this simple example here
Problem: There is a button and onclick of the button it should be updating the count.
without any pattern i can do simply as
window.onload = function() {
var container = document.querySelector('.container');
var count = 0;
container.querySelector('#click').addEventListener('click', function() {
count = +document.querySelector("#count").innerHTML;
count++;
document.querySelector("#count").innerHTML = count;
});
}
<div class="container">
<input type="button" id="click" value="click">Total Counts: <span id="count">0</span>
</div>
In the above link that i have shared about observer pattern it has an implementation for the same using observer pattern jsbin
My Question here, is the usage of a pattern not complicating the code. I am really having a bad time of understanding what exactly the code is trying to solve .can some one please explain this and what is this.notify doing in the jsbin code.
Please help
Thanks
Not an expert in patterns but from what I understand, with simple code like your example that takes in a single event listener, the Observer Pattern would definitely be overkill.
As explained in your link above: "The observer pattern is a simple way to allow communication between elements without having to rely on events, callbacks, or polling. The best thing about the observer pattern is that the thing being observed does not have to worry about what is observing it or how many observers it has." It basically allows you to attach observers easily without having to modify the base element code, because the base code doesn't really have to care about who is watching it. It just has to announce that it's done something (increased a counter property) and it's up to the observers to react accordingly. Because of this, the counter code could stand on it's own and not have any dependencies to run (thus, making it easier to test as well). If you need to make changes to your observers, you won't have to touch the counter code and risk causing any side effects.
In comparison, your example has your callback code and counter heavily tied to one another. If you need to make a change like say, making it have different wording or have the counter value appear under a specific element, you have no choice but to touch that entire block of code. Again though, your code example is simple enough and if that is all it will be doing, then it should be perfectly fine to use.
I think it's easier to understand the concept of the Observer pattern when working with stuff like async code and Promises, where your callbacks/observers become separate from your implementing async code
Firstly, please make sure we are on the same page regarding the terminologies in Observer Pattern (OP): Observer object, Subject (or Observee) object, Subject.addObserver(...) method, and Subject.notify(...) method.
OK, now,
without any pattern i can do simply as
No, you are actually using OP in an implicit form. When you wrote:
container.querySelector('#click')
This will return a reference to the button, I name it button:
var button = container.querySelector('#click');
Then the call button.addEventListener(...) is basically an analogy to Subject.addObserver(...). This means that your button object is actually the Subject in OP. The call Subject.notify(...) is implicitly handled by the JavaScript engine. And your inline function to consume the click event is actually the Observer.
The main difference between your code and the code of jarrettmeyer.com lies in the question: who is the Subject? In jarrettmeyer.com, Subject is not any button but a separated object: the Counter object. This offers some advantages:
The Subject can associate with many buttons, for example, jarrettmeyer can write: $("#anotherButton").on("click", function () { counter.increment(); });
The Subject can easily maintain whatever state and notify whatever info to the Observer. In jarrettmeyer's example, these state/info are simply a count number. Indeed, in your example, no state/info of the button (except the fact that it has just been clicked) is notified since the count number is maintained in your span which belongs to the implementation detail of your Observer and thus not related to OP.
Do you know the code you wrote is also an implementation of the observer pattern? The function you passed after the 'click' argument is an observer and it is added to the observers' array. You can add as many functions as you want against the 'click' event of the same element. They all will be fired by running a loop in the observers' array when the 'click' event happens.
If you have only one action happening as a response to some other action, you can write the action manually without implementing the observer pattern. However, when you want to do multiple things at multiple parts of the codebase in response to some event, observer pattern is the way to go.
Yes, you are right. addEventListener or jQuery .on() could do the similar thing as Observer. They are good enough for most of the front-end usage. But in the following use cases (backend/abstraction), observer pattern is better:
The event being listened is not related to the DOM elements (e.g. JS object's mutation)
You would like to have a better control on removeEventListener (e.g. multiple anonymous callback functions bound on an event type, you would like to move one of them)
The .notify method in the example is made to loop all the callback function in registry array, and try to execute all of them.
Here's a Codepen to show how observer help in the real world.
And here's a simple observer implementation when I learn Observer pattern:
var App = function() {
// This array will store all the subscribers.
this.subscribers = [];
}
// Subscribe, unsubscribe and publish are three base methods in this pattern
App.prototype.subscribe = function(subscriber) {
this.subscribers.push(subscriber);
}
App.prototype.unsubscribe = function(subscriber) {
for (var i = 0; i < this.subscribers.length; i++) {
if (this.subscribers[i] === subscriber) {
this.subscribers.splice(i, 1);
}
}
}
App.prototype.publish = function(message) {
for (var i = 0; i < this.subscribers.length; i++) {
console.log(this.subscribers[i] + ' got ' + message + '!');
}
}
// Testing code.
var myApp = new App();
myApp.subscribe('Timmy');
myApp.subscribe('Tommy');
myApp.publish('a new magazine'); // Both Timmy & Tommy got the new magazine
myApp.unsubscribe('Timmy');
myApp.publish('a new book'); // Now only Tommy got the new book
Attached the Codepen for reference.

How to add action function to Disposable returned from subscribe on an Observable?

You have an Observable, then you subscribe to it. The return value is a Disposable. How do you add a function to be invoked when that Disposable instance is disposed?
clickStream.subscribe(....).dispose();
When dispose() is invoked I want to call a debug function, for example, console.log('Disposing ....'). In a real app, I would want to do some non-rxjs clean-up and UI notification actions. Probably simple thing, but I don't see it in the API. Thx.
I wonder if finally would work for your use case (documentation). According to the documentation, Invokes a specified action after the source observable sequence terminates gracefully or exceptionally. Note that this is not exactly the same as a callback being called when dispose is called but I thought that might be enough for your needs, and you can use it for clean-up actions.
UPDATE
Also close to what you want, but not quite so, you could use the using operator (documentation). It allows to create an object with a dispose method which would be called when the subscription to the associated observable is disposed of.
UPDATE 2
Looking at the source code, finally actually executes the action when the subscription to its observable is disposed. This includes termination or manually calling dispose() on the subscription. That seems very close to what you want to achieve.
Best is to test. Please keep us updated of the results.
Disposables are tied to event sources. The principle is that an event source is interacting with the non-Rx world, and might need to do some clean-up when no one is subscribed any more.
If you could put a hook into the Disposable that is returned from a call to subscribe then it wouldn't be of much use: you know when you called dispose() so you could just handle the clean-up logic there, and if some other code called dispose() it might be on a Disposable further down the chain, which may not affect your subscription.
It sounds like you are trying to make use of side-effects, which isn't necessarily in the spirit of the RxJS API. If you need to close down some network connection or something like that, then maybe you should be using a custom Observable, which creates a Disposable to clean itself up.
A simple example of this (simplified implementation of Observable.fromEvent) might look like this:
function fromEvent(obj, evt){
var subject = new Rx.Subject();
function listener(e){
subject.onNext( e );
}
return Rx.Observable.create( function( observer ){
var disp = subject.subscribe( observer );
obj.addEventListener( evt, listener );
return Rx.Disposable.create(function(){
// All the clean-up code goes here
obj.removeEventListener( evt, listener );
disp.dispose();
})
});
}
var events$ = fromEvent( button, 'click');
var count = 0;
var unsub = events$.subscribe( function(){
console.log('click');
count++;
if( count > 5){
unsub.dispose();
}
})

Can I put a CollaborativeString inside a custom type?

I'm reading the Google Drive Realtime API documentation on Building a Collaborative Data Model.
I really like the way gapi.drive.realtime.databinding.bindString behaves. It doesn't mess up your cursor placement when multiple people are typing in the same text box. But it requires that you pass it a CollaborativeString.
But if you register a custom type, you have to use gapi.drive.realtime.custom.collaborativeField no matter what type of field you are defining, and you can't pass one of these to bindString. In fact, the collaborativeField type does not appear to be documented anywhere, and inspecting it in the console shows that it has no methods. That means there's no registerReference method, which CollaborativeString uses to keep track of cursor positions.
How frustrating. So I guess I have to work around it. I see a few options:
Ignore the fact that the cursor gets messed up during collaboration
Use a CollaborativeMap instead of a custom type, and wrap it with my custom type at runtime
Probably going to do option 2.
I think you misunderstand how this site works, the onus is not on other people to show you how to do something - you're asking other people to take time from their day and help you.
That being said, taking a quick look at the page that you linked shows that what you want to do is not only possible but quite straightforward and compatible with bindString. Stealing from the example code from that page:
// Call this function before calling gapi.drive.realtime.load
function registerCustomTypes()
{
var Book = function () { };
function initializeBook()
{
var model = gapi.drive.realtime.custom.getModel(this);
this.reviews = model.createList();
this.content = model.createString();
}
gapi.drive.realtime.custom.registerType(Book, 'Book');
Book.prototype.title = gapi.drive.realtime.custom.collaborativeField('title');
Book.prototype.author = gapi.drive.realtime.custom.collaborativeField('author');
Book.prototype.isbn = gapi.drive.realtime.custom.collaborativeField('isbn');
Book.prototype.isCheckedOut = gapi.drive.realtime.custom.collaborativeField('isCheckedOut');
Book.prototype.reviews = gapi.drive.realtime.custom.collaborativeField('reviews');
Book.prototype.content = gapi.drive.realtime.custom.collaborativeField('content');
gapi.drive.realtime.custom.setInitializer(Book, initializeBook);
}
and
// Pass this as the 2nd param to your gapi.drive.realtime.load call
function onDocLoaded(doc)
{
var docModel = doc.getModel();
var docRoot = docModel.getRoot();
setTimeout(function ()
{
var book = docModel.create('Book');
book.title = 'Moby Dick';
book.author = 'Melville, Herman';
book.isbn = '978-1470178192';
book.isCheckedOut = false;
book.content.setText("Call me Ishmael. Some years ago - never mind how long precisely - having little or no money in my purse, and nothing particular to interest me on shore, I thought I would sail about a little and see the watery part of the world.");
docRoot.set('tbook', book);
debugger;
}, 0);
}
Good luck and have fun with the Realtime API - it's a lot of fun to play with.
I know this question and answer are getting old, but for reference's sake, just the last part of Grant Watters' very good answer, the onDocLoaded routine, is rather misleading. That function as written, is more suited for the 3rd parameter to the gapi.drive.realtime.load call, the onInitializeModel callback.
The 2nd parameter is called every time the Doc is loaded. You wouldn't normally add the same object over and over as the above routine would... Instead, you would normally set up your event handling, your dataBinds etc. This version might clarify somewhat:
// Pass this as the 2nd param to your gapi.drive.realtime.load call
function onDocLoaded(doc)
{
var docModel = doc.getModel();
var docRoot = docModel.getRoot();
var text = doc.getModel().getRoot().get("text");
// Add an event listener...
text.addEventListener(gapi.drive.realtime.EventType.TEXT_INSERTED, onStringChanged);
// ...and/or bind to collaborative objects:
var textArea = document.getElementById('textArea1')
textBinding = gapi.drive.realtime.databinding.bindString(text, textArea);
etc...
}
Not incidentally, bindString returns the binding object, which is needed to "unbind" later, preventing an AlreadyBound error or other unexpected behavior when the next Doc is loaded. Do something like this:
function onDocLoaded(doc)
{
// Clear any previous bindings etc:
if (textBinding) { textBinding.unbind() };
textBinding = null;
etc...

Categories

Resources