What does RxJS.Observable debounce do? - javascript

Can anybody explain in plain English what RxJS Observable debounce function does?
I imagine it emits an event once in a while depending on the parameters, but my code below doesn't work as I expected.
var x$ = Rx.Observable.fromEvent(window, 'click')
.map(function(e) {return {x:e.x, y:e.y};})
.debounce(1000)
.subscribe(function(el) {
console.log(el);
});
and the JsBin version.
I expected that this code would print one click once per second, no matter how fast I am clicking. Instead it prints the click at what I think are random intervals.

Debounce will emit a value after a specified time interval has passed without another value being emitted.
Using simple diagrams the following may provide greater help:
Stream 1 | ---1-------2-3-4-5---------6----
after debounce, the emitted stream looks like as follows:
Stream 2 | ------1-------------5---------6-
The intermediate items (in this case, 2,3,4) are ignored.
An example is illustrated below:
var Rx = require('rx-node');
var source = Rx.fromStream(process.stdin).debounce(500);
var subscription = source.subscribe(
function (x) {
console.log('Next: %s', x);
}
);
I used node to illustrate this... assuming you have node installed, you can run it by typing
$node myfile.js (where the aforementioned code is in myfile.js)
Once this node program is started you can type values at the console -- if you type quickly items are ignored, and if type intermittently fast and slow items will appear after a gap in typing (in the example above I have 500ms) at the console ("Next: ")
There is also some excellent reference material at https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/debounce.md

Long story short:
debounce waits for X time that the stream isn't emitting any new value, then let the latest value pass.
Long story:
Once a value is emitted, debounce will pause its emission for X time to see if another value is emitted, in fact blocking the stream during this time. If a new value is emitted during the debounce time then the timer is restarted and debounce waits again for the full time.
If its timer expires without any new value being emitted, it let the latest value pass.
Let's say that you want to add autocomplete to an input box. If the user insert "a" you may want to show him the choices "acorn, alaska", but if the user right after press "l" you would propose just "alaska". In this case it's better to wait for the user to stop pressing the keyboards to avoid doing unnecessary work. debounce it's the right tool here: it waits for X time the the stream isn't emitting any new value

.debounce() produces the last received value if no values were received within the specified interval.
It means that as soon as you click within a second - nothing will be produced.
If you want to throttle values to be emitted no more frequent than every second you need to use .sample(1000) instead.

Related

Rx.Js: understanding expand operator

I post data to backend, processing data takes some time and long polling is not a solution in my particular case, so I send request each 5 seconds with expand operator
this.apiService.postData(data).pipe(
expand((status) =>
status.comptete? this.apiService.askStatus(status.request_id).pipe(delay(5000)) : empty()
),
map((result) => {
// processing result here
})
);
The question is how can I make delay be dynamic (e.g. at first time I want to ask for status in 1 second, at second time in 2 seconds and so on)? And two more questions. Have I understood correctly that if I add take(N) operator that will limit askStatus calls to N? Have I understood correctly that I don't need to do any sort of unsubscription here?
expand() passes also index every time it calls the project function so you can calculate delay based on that:
expand((status, index) =>
status.comptete ? this.apiService.askStatus(...).pipe(delay(...)) : empty()
Using take(N) inside expand() won't help because expand() calls the project function on every emission from both source and inner Observables. But you can of course use take(N) after expand().
You don't have to unsubscribe from askStatus() manually if you handle unsubscription later where you also subscribe.

What is wrong with my mapTo Statement

Trying to build something up but getting stuck and missing whatever the error is here. I want to track button clicks but get the value from the #zip input so thats why I chose mapTo. When I remove mapTo I can track clicks and change my statement in the subscribe function to update the result div text with the contents of the #zip input (exactly as I am doing in mapTo) which confuses me because I think that shows the syntax is ok.
Rx.Observable.fromEvent(document.getElementById('btn'),'click')
.mapTo($('#zip').val())
.subscribe(function(zipCode){
$('#result').text(zipCode);
});
jsbin
If I understand what you want to do, you're trying to update $('#result') with current value of $('#zip') at the time the event occurs.
Operator mapTo() takes a single value as argument. This value is then used instead of every value coming from its source. In effect this means that .mapTo($('#zip').val()) is initialized just once when you're creating the chain of Observables (even before you subscribe to it).
If you want it to be always the actual value you'll need some operator that takes a callback as a parameter. For example just map():
Rx.Observable.fromEvent(document.getElementById('btn'), 'click')
.map(function(val) {
return $('#zip').val();
})
.subscribe(function(zipCode){
$('#result').text(zipCode);
});
Your updated demo: http://jsbin.com/qepalo/1/edit?html,js,console,output

Be secured from perform one action twice

I have application on Node.js with using Express as API route and MongoDB as DB.
I have a raffle. User must join raffle only one time.
I am currently using an array in memory with participating, but if you will make two request to API at the same time, user will be joined in raffle two times.
How can i disallow to join raffle more than one time?
You would need to structure your workflow to support idempotent operation. That is, performing the same operation twice does not change the result.
For example, incrementing a variable is not idempotent, since calling the increment function twice resulted in the variable get incremented twice (e.g. calling x++ twice will result in adding 2 to x). I think this is the essence of your current design, where you mentioned: " if you will make two request to API at the same time, user will be joined in raffle two times".
An example of an idempotent operation is setting a variable to a value. For example, calling var x = 1 multiple times will only result in value 1 getting assigned to x. No matter how many times you call that line, x will always be 1.
Some resources to help get you started:
What is Idempotency
Database operation that can be applied repeatedly and produce the same results
What is an idempotent function
How to Write Resilient MongoDB Applications
You should maybe store the array in mondodb, you don't want to loose this list if node restart.
About the "join two time" problem, just throttle the client side function that make the request to your API, so that it can only be called one time during the time passed in your throttle function.
Exemple throttle function :
function throttle (callback, limit) {
var wait = false;
return function () {
if (!wait) {
callback.apply(this, arguments);
wait = true;
setTimeout(function () {
wait = false;
}, limit);
}
}
}
and now your request function :
var throttleApiFunc = throttle(apiFunc, 5000);
// this can only trigger one time each 5 seconds
throttleApiFunc();

Search & LazyLoad can't keep up w/ typing speed?

So, all of the code works quite well. A database a queried, the node/parent IDs are lazily passed to jsTree, and, if one types a term into the search bar—a similar process goes on, but the nodes passed to jsTree are those returned by another SQL query (using something like SELECT nodeID FROM table WHERE name LIKE %searchTerm%).
There's only one problem:
If I type too quickly into the search bar, the results get all mixed up with one another. If I type slowly (I'd estimate 2 letters a second max), everything works well. Any faster and everything is blurred together. (That is, if I'm searching for names which contain the term "test", and type this quickly, I'll get names that contain "t", "te", "tes", and "test" instead of just names that contain "test".)
Anyone have this problem before? Is there some "wait until ready" function I should be using?
I can suggest you to do the 'Timeout' workaround. Basically, you create a SetTimeout function with a delay of 200-400 miliseconds and launch your lazyload ajax there. Every inputbox.change event restarts the time. See example in pseudo-javascript:
$('#your-input').keydown(function(){
if(ajaxTimer != undefined) {
clearTimeout(ajaxTimer);
}
ajaxTimer = setTimeout(function(){
$.ajax({...}).done(function() {...}
},400);
})
Use a throttle on the typing input, basically guarantees that a function will be called a maxmimum of once every X seconds. Sure you could write your own however there is a great library out there already.
check it out if you're interested http://benalman.com/projects/jquery-throttle-debounce-plugin/

How can I get the newly entered text from a textarea?

I want to make a chat program like Google Wave that shows what is typed in real-time. For the moment I'm just using a textarea. I just want to know what change the user made to the textarea (either one character or pasted text) without having to perform a diff on the whole textarea. I was something along the lines of using a setTimeout with the oninput event but I believe this could only give me the "before text" and the "after text". Any ideas?
this type of functionality is most likely accomplished using a message batching type setup.
here's how it would break down:
attach event handlers to a text area to track modifications on the character level (think, keydown, keypress, keyup, etc..) and log these into a message buffer
you'll want to have a backup for "end transaction" type events like "onchange", "onpaste", etc.. that serve to check the integrity of you're state and be prepared to run a "full re-sync" (eg. signal other clients to do a full "pull") if you think you have a mismatch
on an interval (every 0.3 - 1 second), you empty the message buffer and re-transmit the messages to other clients (direct connect [websockets?], or indirect via server)
when a client receives messages, they process them in the same order they've received them [hopefully] ending up with the same state, error or conflict fallback: signal full sync
on a full sync client should re-pull the full state and attempt to place the focus/carrot as close to the last position as possible
on a side note: this is greatly simplified with a concept of "regions" where you can do clean full swaps on your region without affecting mine...
hope this helps -ck
You will want to look at the key events: keydown, keypressed, keyup
W3C keyboard events
But you also need to consider pasted text – which might not have a key press.
One strategy, to simplify event handeling, would be to have a 'submit' button. Then you will know when the user is done with their statement.
Track the value of your text area with setInterval and compare it against itself to see if it changes. This works for pastes, normal key presses, etc.
Example:
var lastVal = $("#myTextInput").val();
setInverval(function(){
if($("#myTextInput").val() != lastVal) {
lastVal = $("#myTextInput").val();
// the text changed, do something with the current value (AJAX, whatever)
}
}, 300);

Categories