I have ASP Web Forms Web application, which is used for searching in my database. I have page which contains gridview and also text input field for filtering results.
Input field has onkeyup event which run postback by JavaScript for refreshing the gridview from codebehind.
And also in codebehind I have method, which does saving state last filter request, and if request do not equal previously filter request, then I run new query to database.
My problem - onkeyup event on text input field may generate multiple postbacks. If it first postback, then server will execute the query to database. But following postbacks do not execute and overlap first results. And therefore my gridview stay old state.
<script type="text/javascript">
function DoUpdateGridView() {
var timeout = null;
clearTimeout(timeout);
timeout = setTimeout(function () {
Sys.WebForms.PageRequestManager.getInstance()._doPostBack('<%=GUIGridUpdatePanel.ClientID%>', '<%=GUIGridUpdatePanel.UniqueID%>');
}, 1000);
};
</script>
How you may see I set delay for execute JavaScript, but this only Delays execution of the code but does not interrupt.
I think i may make cache first results, and return it.
But I need know all solutions for this problem.
Сan there be any way to perform only the latest event from the JavaScript?
But then somebody may send multiple request and my web application may fall (DDOS).
You can't interrup what is already happening on the server, but there is an issue with your "delay" code. You need to persist the timeout variable between calls.
Try:
<script type="text/javascript">
//New Position for timeout variable
var timeout = null;
function DoUpdateGridView() {
//This will now clear the existing timneout function
clearTimeout(timeout);
timeout = setTimeout(function () {
//For debug purposes only
console.log("About to postback");
Sys.WebForms.PageRequestManager.getInstance()._doPostBack('<%=GUIGridUpdatePanel.ClientID%>', '<%=GUIGridUpdatePanel.UniqueID%>');
}, 1000);
};
</script>
When two (or more) postbacks are made from the same client at the same time, they send the same ViewState data to the server as part of each request. Control state is part of ViewState. Assuming Session is being handled in the default manner, these requests are processed by the server serially.
Let's say the server alters controls while handling the first request. Then the second request is processed. But since both postbacks were generated at the same time, the second request has the same ViewState (and thus the same control state) as the first request, so the state of the controls that the server sees is the state before the first request was processed, and not the state after the first request was processed!
The simplest solution is to store the pieces of state you are using to determine whether to change the controls in the Session, which by default is stored in memory on the server, thus making the two requests distinguishable.
Related
I am trying to update a document using rpc service.
I have an xpage with a button. I want to click that button and by using CS javascript to setInterval and update a document field every 5 seconds.
I used the remote service control and the code is this:
<xe:jsonRpcService id="jsonRpcService1" serviceName="heartBeat">
<xe:this.methods>
<xe:remoteMethod name="send">
<xe:this.script><![CDATA[var db = session.getCurrentDatabase();
var heartView = db.getView("Heartbeats");
var doc:NotesDocument = heartView.getFirstDocument();
var vl = doc.getItemValueString();
if(vl==""){
doc.replaceItemValue("dummyH","z");
}else{
doc.replaceItemValue("dummyH",vl+"z");
}
doc.computeWithForm(false, false);
doc.save();]]></xe:this.script>
</xe:remoteMethod>
</xe:this.methods></xe:jsonRpcService>
The code of the button is this:
<xp:button value="Label" id="button1">
<xp:eventHandler event="onclick" submit="false">
<xp:this.script><![CDATA[setInterval(function(){heartBeat.send()},3000);]]></xp:this.script>
</xp:eventHandler></xp:button>
It doesnt work. I get no error but i also get no updates in the document... What is wrong in the code?
UPDATE
if i replace rpc script with a simple print("aaaaa") it works perfectly (i also followed the suggestions of Tim in the answer below, for my client side code).
when i use again my previous code i get the following javascript error:
Unable to load /Databases/Test/Mike/my1.nsf/RPCpage.xsp/mm?$$viewid=!dqducrahog! status:400
how can i access the database and documents from a remote service?
The syntax of RPC calls is slightly unusual; your call to heartbeat.send(), for example, does not actually call the remote send method. Rather, it returns a handle on a remote method descriptor... to actually call the remote method, you need to add a callback:
var remoteMethod = heartbeat.send();
remoteMethod.addCallback(function(response){
// handle whatever came back from the server
});
If, for instance, you designed your remote method to return whether or not the heartbeat was successful, a more complete example would look like this:
var heartbeatInterval = 3000;
var periodicHeartbeat = function() {
var remoteMethod = heartbeat.send();
remoteMethod.addCallback(function(response){
if (response.stillAlive) {
setTimeout(periodicHeartbeat, heartbeatInterval);
}
});
}
setTimeout(periodicHeartbeat, heartbeatInterval);
It's generally recommended to use setTimeout to initially schedule a deferred function, and then call setTimeout again from within that deferred function to schedule its own next execution, instead of using setInterval.
In this use case, however, this is particularly true because each remote method call is asynchronous, so with setInterval, you would eventually end up with request overlap, because each call requires at least a few milliseconds to complete.
Additionally, the preferred pattern of scheduling the next execution from inside the current allows you to check what the server sent back to determine whether to even bother continuing to send subsequent requests. Right now you're not returning any value from your remote method; if you change just the last line to:
return { stillAlive: doc.save() };
...then that JSON object will be automatically passed to your callback function as its first argument. Taking another look at one line from the prior example:
if (response.stillAlive) {
...so the RPC call only reschedules itself if the prior call succeeded. This probably isn't the precise behavior you want, but given that you can return data from the remote method, and the data that was returned is passed to your callback function, you can design a call and response approach that best fits your business specifications for this functionality.
Having said all that, you may want to take a look at the keepAlive component from the XPages Extension Library... it doesn't store heartbeat data in Domino documents, but if all you're trying to do is prevent idle sessions from expiring, it's a much simpler solution.
I have an app that loads several resources when it's first run, which are stored in localStorage. I have a function that checks whether all the local storage variables are set, so that part is working okay.
My method of working is like this:
Display a loading message.
Initialize the AJAX requests.
Start a timer interval to check if everything has loaded.
When the data has loaded, initialize the application etc.
If the data did not load, display an error message.
The problem is with #5 - how to detect if there was an error? For example if there was a connection problem or the sever sent back invalid data for whatever reason. Here is my current code - downloadData just performs a basic AJAX request:
// check local storage and download if any missing
if ( !checkLocalStorage() )
{
$('#content').before( '<div class="notice" id="downloading">Downloading data, please wait...</div>' );
for ( var i in db_tables )
{
if ( localStorage[db_tables[i]] == null )
downloadData( db_tables[i] );
}
}
// check progress
var timer = setInterval( function() {
if ( checkLocalStorage() )
{
// everything is downloaded
$('#downloading').hide();
clearInterval(timer);
initApp();
}
}, 500 );
Could you turn it around a bit? Something like this (with sensible variable names and a "real" API) would simplify things:
Display a loading message.
Instantiate an application initializer, ai.
Crank up the AJAX requests:
Success handlers call ai.finished(task).
Error handlers call ai.error(task).
Register with the initializer, ai.register(task), in case a "you're taking too long" check is desired.
Once all the AJAX requests have called ai.finished, initialize the application etc.
If any of the AJAX tasks called ai.error, then display an error message and start cleaning things up.
This way you wouldn't need to setInterval() and the individual AJAX tasks will tell you when they have finished or fallen over. You might still want the interval to deal with tasks that are taking too long but most of the logic would be notification based rather than polling based.
Seeing your actual ajax calls in downloadData would help, but I suggest you look over the jquery AJAX API again. Ajax calls have callbacks not just for overall completion but specifically for success and failure including errors. Try to do something like retrying if there is an error and if it continues to fail you can warn the user. You can also use these callbacks to notify your application when the loading is done instead of using an interval timer.
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/
How to call or make the javascript function from the Application_Start of global.asax in asp.net mvc(C#) application?
You can remember the last "invoked" time in Session or cookies (which is easier for javascript but worse for performance/etc) and then
function check() {
// or var lasttime = <%= Session["lasttime"] %>;
if (now - $.cookie("lasttime") > timeout)
{
$.cookie("lasttime", now);
performAction();
}
window.setTimeout(check, 1000);
}
You can call time function once from $(document).ready().
But note that it may take browser several seconds to render page, or it may bump into 404 or other errors and page will be inactive... javascript is not a reliable way to do scheduled actions.
Another way is to have your timer on server. JavaScript function like above will just ask for it from time to time, passing user ID or something like that. This will prevent timer reset during page reload. But you'll have to do request too often. So the best solution would be to combine two techniques:
Run timer on server
When page is renders, set var inited = false;
Run function above but like this: if (!inited) timer = $.getJSON("/timer?uid=x"); and when you have the precise current timer you can continue with JavaScript only, without server requests.
"The javascript function gets the data to be shown to the User from database through jquery. The javascript function will be executed periodically using setTimeout"
This wouldnt be the place to do it.
Have you thought about using your masterpage?
Since JavaScript executes on client side and global.asax executes on server side. You cannot do that.
How about you check a Application level variable at the load of your landing page (master page would also do) and register whatever the javascript there and set the variable.
You can skip the registration if the variable is set.
My users are presented a basically a stripped down version of a spreadsheet. There are textboxes in each row in the grid. When they change a value in a textbox, I'm performing validation on their input, updating the collection that's driving the grid, and redrawing the subtotals on the page. This is all handled by the OnChange event of each textbox.
When they click the Save button, I'm using the button's OnClick event to perform some final validation on the amounts, and then send their entire input to a web service, saving it.
At least, that's what happens if they tab through the form to the Submit button.
The problem is, if they enter a value, then immediately click the save button, SaveForm() starts executing before UserInputChanged() completes -- a race condition. My code does not use setTimeout, but I'm using it to simulate the sluggish UserInputChanged validation code:
<script>
var amount = null;
var currentControl = null;
function UserInputChanged(control) {
currentControl = control;
// use setTimeout to simulate slow validation code
setTimeout(ValidateAmount, 100);
}
function SaveForm() {
// call web service to save value
document.getElementById("SavedAmount").innerHTML = amount;
}
function ValidateAmount() {
// various validationey functions here
amount = currentControl.value; // save value to collection
document.getElementById("Subtotal").innerHTML = amount;
}
</script>
Amount: <input type="text" onchange="UserInputChanged(this)">
Subtotal: <span id="Subtotal"></span>
<button onclick="SaveForm()">Save</button>
Saved amount: <span id="SavedAmount"></span>
I don't think I can speed up the validation code -- it's pretty lightweight, but apparently, slow enough that code tries to call the web service before the validation is complete.
On my machine, ~95ms is the magic number between whether the validation code executes before the save code begins. This may be higher or lower depending on the users' computer speed.
Does anyone have any ideas how to handle this condition? A coworker suggested using a semaphore while the validation code is running and a busy loop in the save code to wait until the semaphore unlocks - but I'd like to avoid using any sort of busy loop in my code.
Use the semaphore (let's call it StillNeedsValidating). if the SaveForm function sees the StillNeedsValidating semaphore is up, have it activate a second semaphore of its own (which I'll call FormNeedsSaving here) and return. When the validation function finishes, if the FormNeedsSaving semaphore is up, it calls the SaveForm function on its own.
In jankcode;
function UserInputChanged(control) {
StillNeedsValidating = true;
// do validation
StillNeedsValidating = false;
if (FormNeedsSaving) saveForm();
}
function SaveForm() {
if (StillNeedsValidating) { FormNeedsSaving=true; return; }
// call web service to save value
FormNeedsSaving = false;
}
Disable the save button during validation.
Set it to disabled as the first thing validation does, and re-enable it as it finishes.
e.g.
function UserInputChanged(control) {
// --> disable button here --<
currentControl = control;
// use setTimeout to simulate slow validation code (production code does not use setTimeout)
setTimeout("ValidateAmount()", 100);
}
and
function ValidateAmount() {
// various validationey functions here
amount = currentControl.value; // save value to collection
document.getElementById("Subtotal").innerHTML = amount; // update subtotals
// --> enable button here if validation passes --<
}
You'll have to adjust when you remove the setTimeout and make the validation one function, but unless your users have superhuman reflexes, you should be good to go.
I think the timeout is causing your problem... if that's going to be plain code (no asynchronous AJAX calls, timeouts etc) then I don't think that SaveForm will be executed before UserInputChanged completes.
A semaphore or mutex is probably the best way to go, but instead of a busy loop, just use a setTimeout() to simulate a thread sleep. Like this:
busy = false;
function UserInputChanged(control) {
busy = true;
currentControl = control;
// use setTimeout to simulate slow validation code (production code does not use setTimeout)
setTimeout("ValidateAmount()", 100);
}
function SaveForm() {
if(busy)
{
setTimeout("SaveForm()", 10);
return;
}
// call web service to save value
document.getElementById("SavedAmount").innerHTML = amount;
}
function ValidateAmount() {
// various validationey functions here
amount = currentControl.value; // save value to collection
document.getElementById("Subtotal").innerHTML = amount; // update subtotals
busy = false;
}
You could set up a recurring function that monitors the state of the entire grid and raises an event that indicates whether the entire grid is valid or not.
Your 'submit form' button would then enable or disable itself based on that status.
Oh I see a similar response now - that works too, of course.
When working with async data sources you can certainly have race conditions because the JavaScript process thread continues to execute directives that may depend on the data which has not yet returned from the remote data source. That's why we have callback functions.
In your example, the call to the validation code needs to have a callback function that can do something when validation returns.
However, when making something with complicated logic or trying to troubleshoot or enhance an existing series of callbacks, you can go nuts.
That's the reason I created the proto-q library: http://code.google.com/p/proto-q/
Check it out if you do a lot of this type of work.
You don't have a race condition, race conditions can not happen in javascript since javascript is single threaded, so 2 threads can not be interfering with each other.
The example that you give is not a very good example. The setTimeout call will put the called function in a queue in the javascript engine, and run it later. If at that point you click the save button, the setTimeout function will not be called until AFTER the save is completely finished.
What is probably happening in your javascript is that the onClick event is called by the javascript engine before the onChange event is called.
As a hint, keep in mind that javascript is single threaded, unless you use a javascript debugger (firebug, microsoft screipt debugger). Those programs intercept the thread and pause it. From that point on other threads (either via events, setTimeout calls or XMLHttp handlers) can then run, making it seem that javascript can run multiple threads at the same time.