I am using angular 8.
There is one auto-complete input and if it's value changes I have to make API call and load new suggestions for this input.
//In Template
<autocomplate [suggestions]="suggestions" (filterChange)="filterChange($event)"></autocomplate>
//In Component
filterChange(e) {
console.log(e)
this.loadSubscriptions(e ? { 'filterItem.name': e } : {})
}
loadSubscriptions(params) {
if (this.suggestionsSubscriber) this.suggestionsSubscriber.unsubscribe()
this.suggestionsSubscriber = this.suggestionsService.loadData(params).subscribe(
data => this.suggestions = data
})
}
Everything works fine, but the problem is when user types fast application makes to many requests.
Can I somehow delay requests if user types fast? for example, while the user is typing don't make API calls on every change, and if the user stops typing then make API call.
Or if you have a better way to solve this problem, please share.
Use RXJS denounceTime operator. Simply chain it to your Observable.
Whenever debounceTime receives an event, it waits a designated amount of time to see if another event comes down the pipe. If it does, it restarts its timer. When enough time has passed without another event streaming in, it emits the latest event.
I would suggest you to use throttle or debounce. You can write your own implementation for those or use library such as lodash.
Debounce using latest Rxjs can be a work around. Please see below for implementation.
Angular and debounce
I also had a same problem, so i put my code inside setTimeout as below
filterChange(e) {
console.log(e)
setTimeout(()=>{
this.loadSubscriptions(e ? { 'filterItem.name': e } : {})
},2000);
}
Now if you type very fast then it will not call the loadSubscriptions at that time. it will call after 2 sec.
You can configure the time according to your choice.
I hope This will helps you.
Related
What is the difference between autorun and reaction? I'm looking for a full explanation that helps to decide when to use which. Is it possible that reaction can even trigger while autorun does not? How they are related to each other?
Are these both the same?
autorun(() => {
localStorage.setItem("current_order", JSON.stringify(this.currentOrder))
})
reaction(
() => JSON.stringify(this.currentOrder),
(json) => {
localStorage.setItem("current_order", json)
}
)
As far as I know, the first parameter of reaction will evaluate and check for changes, so it is desired for computed values, but shouldn't autorun just work fine if this.currentOrder is observable? Will JSON.stringify(this.currentOrder) in reaction run, even when this.currentOrder is observable and has not changed?
autorun runs immediately after it was initialised, reaction runs only after the data expression has changed after initialisation.
Also, because reaction runs only when data expression changes you can use other observables in the effect function, which would not work for autorun because it subscribes for all observables you use inside of it, which means reaction gives more fine grained control.
EDIT:
Adding an example to make it more clear
Consider this example, you have two observables A and B, and want to do something with B when A changes, but don't want to do anything when B changes. If you just make autorun it will run when any of the observables changes, so it's not what you want. But with reaction it's possible to only react to the changes of A (by only using A in expression function).
MobX works like a spreadsheet, everything is derived from state. Even effects with things like autorun.
The problem for me is that it's unclear when to run an effect based on some event firing, rather than based on a state-change (using autorun or something like that).
For instance let's say I need to fetch some milk (an action that causes an HTTP request - hence an effect) after 3 specific conditions are met (derived from state). I can use when to run this side-effect:
when(
() => {
if (
self.isOnConfirmationPage &&
paymentStore.successfullySubscribed &&
calendarStore.isFriday
) {
return true;
}
return false;
},
() => self.fetchMilk(),
);
A new requirement comes in asking me to add some logging. Specifically I need to call a track() service (another HTTP request hence another effect), but this track() service depends on the data returned by the fetchMilk() service.
Now I could simply add .then() to my fetchMilk:
self.fetchMilk().then(milkResponse => self.track(
milkResponse.user,
milkResponse.order
))
Going back to the title of my question, this to me is "reacting based on an event" - the event in this case being the response by the fetchMilk() service.
What if I simply react based on the state-change rather than the event?
This means track() needs to be also placed in a reaction, and since it depends on the response of fetchMilk() I can simply store this in a MobX store and react upon it:
when(
() => {
if (
self.milk.user &&
self.milk.order
) {
return true;
}
return false;
},
() => self.track(self.milk.user, self.milk.order),
);
Note that instead of using a "promise-based" flow for handling async behavior, I'm simply reacting based on values changing.
My question: which of these two options should I use? What are the benefits/cons of each approach? Is it safe to model async behavior using the 2nd approach?
I would go for the promise.then() version, simply because it is easier to follow the code flow.
There is no rule that says that everything in the app must be set up so that all code is driven by mobx actions and reactions.
When you want a change in the app to be broadcasted to the world, then you enter the mobx world.
When you think about it, mobx is just like an event dispatcher, but the event dispatch code (subscriptions and notifications) are hidden behind mobx magic.
So the best option in my experience is to do all the async work you need to do, and then notify mobx of the changes.
If you take a look at the official documentation it has an example of using asynchronous code, which does exactly that.
If I have a service containing an Observable and I have the following code...
this.collapse.newChild.subscribe(name => {
if (name === "helpArea") {
this.collapse.process("helpArea", true);
}
// I would like to detach now
});
Is it possible to detach without canceling the original subscription? If I call unsubscribe then when I call this.collapse.newChild.next("thing") it throws an error Error: object unsubscribed.
So I don't think unsubscribe is what I am looking for, so is there a way to detach a single observer?
As a work around for now I am using...
this.collapse.newChild.map(name => name === "helpArea").first().subscribe(...)
Peter answer is correct from a design/practice PoV, but beware also that you get this error because you unsubscribe the subject.
What you want to stop is the subscription instead:
var subscription = this.collapse.newChild.subscribe(...)
// NOT this
// this.collapse.newChild.unsubscribe()
// this
subscription.unsuscribe()
This will kill/stop not the subject, but only your current subscription to it.
I don't think your "workaround" is at all bad. It even seems idiomatic: the way that Rx wants you to do it. But you can write it a bit shorter, using the form of first that takes a predicate.
this.collapse.newChild
.first(name => name === "helpArea")
.subscribe(...);
In general, the mindset to get into with Rx is to as much as possible use combinators to manipulate streams to produce exactly the values that you need, and then subscribe with a simple handler that applies the final, effectful, step: updating the UI, calling a service etc. This will lead to cleaner, more expressive code, that is easier to read and easier to re-use.
Another nice thing about this approach for your example is that there is no need to unsubscribe. RX will do that for you after the first value because it knows there won't be more. So it's much cleaner than doing it yourself.
Calling unsubscribe() on a Subscription object unsubscribes only this particular observer. So a question is how did you unsubscribed at the first place because using first() sends complete notification which causes unsubscription anyway.
Btw, you can always unsubscribe inside your own subscriber's callback:
var subscription = this.collapse.newChild.subscribe(name => {
if (name === "helpArea") {
this.collapse.process("helpArea", true);
}
subscription.unsubscribe();
});
This is a really basic JavaScript question and probably duplicate, but I don't know the answer!
I have code as follows:
function userlist_change(myval, function_type) {
// relatively slow code involving Ajax call
// based on Ajax results, change some client-side stuff
}
$("#subjectlist").change(function() {
userlist_change($("#subjectlist").val(), 'change');
}).change();
$("#subjectlist").keypress(function() {
userlist_change($("#subjectlist").val(), 'keypress');
});
I have the problem that if the .change() event is called, the userlist_change function kicks off, and it's relatively slow. If the user changes the list again (e.g. by typing), my code waits for userlist_change to complete before restarting it with the new value.
This looks quite odd in the UI, as it can take a few seconds for anything to change client-side - and sometimes the results of the first call only appear after the user has already made a second call.
Is there any way I can interrupt any existing userlist_change process when the .change() or `keypress() event is fired?
[EDIT] What would be ideal is a simple 'kill any running functions with this name' command - is this possible? Or do I really have to fiddle around with timers?!
you can store last request time in a global variable, and store a request time in each ajax request, so that when you are just showing the result of first request, if the global last request time is greater than request, request time, you should show, other wise not. For example:
var lastRequestTime;
function userlist_change(myval, function_type,requestTime) {
// relatively slow code involving Ajax call
// based on Ajax results, change some client-side stuff
if(lastRequestTime <= requestTime){
//show
}
}
$("#subjectlist").change(function() {
lastRequestTime = new Date();
userlist_change($("#subjectlist").val(), 'change',lastRequestTime );
}).change();
$("#subjectlist").keypress(function() {
lastRequestTime = new Date();
userlist_change($("#subjectlist").val(), 'keypress',lastRequestTime );
});
You should use throttling of event. It is quite easily done with RX for JavaScript, but library is quite complicated. You can try filter value with timer.
Here is useful plugin for throttling: http://benalman.com/projects/jquery-throttle-debounce-plugin/
I'm using jQuery to change the HTML of a tag, and the new HTML can be a very long string.
$("#divToChange").html(newHTML);
I then want to select elements created in the new HTML, but if I put the code immediately following the above line it seems to create a race condition with a long string where the changes that html() is making may not necessarily be finished rendering. In that case, trying to select the new elements won't always work.
What I want to know is, is there an event fired or some other way of being notified when changes to html() have finished rendering ? I came across the jQuery watch plugin, which works alright as workaround but it's not ideal. Is there a better way ?
As a commenter already mentioned, JavaScript is single threaded, so you can't get race conditions.
What may trip you up however, is the fact that the UI will not update itself based on JavaScript, until a thread is finished. This means that the entire method must finish, including all code after you call html(...), before the browser will render the content.
If your code after calling html(...) relies on the layout of the page being recalculated before continuing, you can do something like this:
$("#divToChange").html(newHTML);
setTimeout(function() {
// Insert code to be executed AFTER
// the page renders the markup
// added using html(...) here
}, 1);
Using setTimeout(...) with a time of 1 in JavaScript defers execution until after the current JavaScript code in the calling function finishes and the browser has updated the UI. This may solve your problem, though it is difficult to tell unless you can provide a reproducible example of the error you're getting.
use .ready jQuery function
$("#divToChange").html(newHTML).ready(function () {
// run when page is rendered
});
It's 7 years latter and I just ran into a scenario exactly like the one #mikel described, where I couldn't avoid a "timer based solution". So, I'm just sharing the solution I developed, in case anyone out there is still having issues with this.
I hate having setTimeouts and setIntervals in my code. So, I created a small plugin that you can put where you think it's best. I used setInterval, but you can change it to setTimeout or another solution you have in mind. The idea is simply to create a promise and keep checking for the element. We resolve the promise once it is ready.
// jquery.ensure.js
$.ensure = function (selector) {
var promise = $.Deferred();
var interval = setInterval(function () {
if ($(selector)[0]) {
clearInterval(interval);
promise.resolve();
}
}, 1);
return promise;
};
// my-app.js
function runWhenMyElementExists () {
// run the code that depends on #my-element
}
$.ensure('#my-element')
.then(runWhenMyElementExists);