How to implement super fast ASP.NET AJAX callbacks/PageMethods - javascript

I am designing an ASP.NET web application (.NET 4.0) which basically has a page that should interact with the code behind every 1-2 seconds (Using Client callbacks or PageMethods via ScriptManager or jQuery.ajax) It'll be hosted on an intranet, so a 1-2 second refresh rate is kind of reasonable.
How can I make the page to access the web service/pagemthod in the code behind in a timeply manner (e.g. every 1 second). Should I use a javascript timer (I'm not familiar with javascrip very much)?
Although the site is hosted on an intranet, but I still need to implement a good approach to reach the desired refresh rate. the amount of data being transfered is about 1KB in each interaction. What are your recommendations on my design regarding this? (using callbacks or ScriptManager or jQuery.ajax,... any pitfalls I should avoid)
Thanks.

A 1kb request every 1-2 seconds is reasonable using either approach. A page method or web service (they're nearly identical under the hood) that does nothing will respond in just a few milliseconds if you're dealing with a fast network/server.
The limiting factor will be how long the meat of your server-side method takes to complete (i.e. if it's involves a database or external service, that's going to slow you down more than the overhead of the service).

I use Webservices, since they are much faster. But if you doing with UpdatePanels, Webservices are useless. Futher I would say, you shouldnt update the page every x seconds, but first ask, if there is an update to do at all. This saves a lot ;-)
This might be a little example, i didn´t try but worked once like this. Is a ms ajax version, needs scriptmanager
Type.registerNamespace("myproject");
myproject.updateControl = function () {
myproject.updateControl.initializeBase(this);
this._xhr = null;
this._updating = false;
this._timer = null;
}
myproject.updateControl.prototype = {
initialize: function () {
myproject.updateControl.callBaseMethod(this, 'initialize');
this.startTimer();
},
startTimer: function () {
if (this._timer) clearTimeout(this._timer);
this._timer = setInterval(Function.createDelegate(this, this._timerWork), 2000);
},
stopTimer: function () {
clearTimeout(this._timer);
this._timer = null;
},
_timerWork: function () {
if (this._updating || !this._checkXhr()) return;
this._xhr = Sys.Net.WebServiceProxy.invoke("myServicePath Or null if PageMethod", "checkForUpdate",
false,
null,
Function.createDelegate(this, this._onCheckedUpdate));
},
_onCheckedUpdate: function (data) {
this._xhr = null;
if (data.needsUpdate) {
this._update();
}
},
_udpate: function () {
if (!this._checkXhr) return;
this._updating = true;
this._xhr = Sys.Net.WebServiceProxy.invoke("servicepath", "updateMe", false, { param: param }, Function.createDelegate(this, this._updateSucces));
},
_updateSuccess: function (data) {
alert("yeah i´m get updated");
this._updating = false
this._xhr = null;
},
_checkXhr: function () {
if (this._xhr()) {
if (confirm("There is an active call to the Server. If you wait to long, it may have been broken. Do you want to Abort the current call?")) {
this._xhr.get_executor().abort();
this._xhr = null;
return true;
} else {
return false;
}
}
return true;
},
dispose: function () {
myproject.updateControl.callBaseMethod(this, 'dispose');
}
}
myproject.updateControl.registerClass('myproject.updateControl', Sys.Component);
usage
$create(myproject.updateControl);
or
var upd = new myproject.updateControl();
upd.initialize();

"Timer" on the client side is a bad idea. You can use setInterval(method, timespan) to force a call every n milliseconds, but if the server ever gets backed up, you can then start stacking requests and you'll start getting responses out of order (even on a non-slow network).
I recommend using setTimeout(method, timespan) in your ajax code in the call processing logic to setup the next call.
Example (using jQuery):
function getStuff()
{
$.get(
'myurl.aspx?r=' + Math.random(), // stop caching issues
function(data) {
$('#myDiv').html(data);
setTimeout(getStuff, 2000); // you might want to set this to 1900 if you need it closer to every 2 seconds
}
);
}
setTimeout(getStuff, 2000); // the initial timer initialization

Related

jQuery prevent reload if button clicked

I have a jQuery datatable that immediately loads ON READY. After that, the datatable is reloaded every 30 seconds. This feature is functioning properly.
I have added a search feature that automatically reloads the datatable with new search results. This part is also functioning properly.
The problem I am experiencing is when I am using the search feature, and the new search results are returned. After 30 seconds, the new results are cleared and the datatable reloads with all of the original records.
Here is what I am currently attempting:
$(document).ready(function()
{
var searchCriteria = "";
displayBookings(searchCriteria);
var idle = 0;
var idleInterval = setInterval(timer, 30000);
$(this).mousemove(function(e){idle = 0;});
$(this).keypress(function(e){idle = 0;});
function timer()
{
idle = idle + 1;
if(idle > 2)
{
displayBookings(searchCriteria);
console.log('table reloaded');
}
}
$('#searchPending').on('click', function()
{
var isPending = 'Y';
var searchCriteria = {
isPending: isPending
};
displayBookings(searchCriteria);
});
});
The function displayBookings() takes searchCriteria. If searchCriteria is blank, then a basic query is fired. Obviously is searchCriteria contains parameters, then the same query is fired with a WHERE clause attached. I did not disclose the code for displayBookings().
All I need to do is stop the 30 second interval if the #searchPending button is clicked.
Clear the interval so it will stop loading.
clearInterval(idleInterval)
specifically in your code:
$('#searchPending').on('click', function()
{
clearInterval(idleInterval)
var isPending = 'Y';
var searchCriteria = {
isPending: isPending
};
displayBookings(searchCriteria);
});
Rather than start and stop the timer interval, since you'll run into a bit of a race condition, you can just have the "refresh" (your "timer" function) refresh using the latest search criteria. To do this, just pass the same object into your displayBookings function. E.g.
const search = { criteria: "" };
$(...).click(() => {
search.criteria = 'change it...';
displayBookings(search.criteria);
});
setInterval(() => displayBookings(search.criteria), 30000);
This way, if a refresh happens, it will use the latest search.criteria. You can achieve the same result with minimal change in your code by simply removing the var from the second searchCriteria. Currently, without removing the var, your outer criteria is being "shadowed" by your inner.
I alluded to debouncing1 in one of my comments. I misread the code and debouncing is not what you want. Instead, you want to only "refresh" if there hasn't been any user activity within some threshold. Here's an alternative from the approach you used:
let lastInteraction = 0;
function interact() {
lastInteraction = Date.now();
}
$(this).mousemove(interact);
$(this).keypress(interact);
Then in your refresh function:
if (Date.now() - lastInteraction > threshold) { ...
Implementing both the central criteria and revised idle check:
$(document).ready(function() {
const idle = {
threshold: 1000,
lastInteraction: 0,
interact() {
idle.lastInteraction = Date.now();
},
isIdle() {
return Date.now() - idle.lastInteraction > idle.threshold;
}
};
const search = { criteria: "" };
$(this).mousemove(idle.interact);
$(this).keypress(idle.interact);
setInterval(() => {
if (idle.isIdle()) {
displayBookings(search.criteria);
}
}, 30000);
$('#searchPending').on('click', () => {
search.criteria = { isPending: 'Y' };
displayBookings(search.criteria);
});
displayBookings(search.criteria);
});
1 The Wikipedia article linked to discusses debouncing with a keyboard. It's the same concept. You'd use debouncing on your displayBookings function if you plan on having it execute live as the user is typing. This would prevent too many HTTP requests from happening in a short duration of time.

How can I detect that new content has been loaded in response to a scroll trigger?

I'm running a script on Facebook that requires me to get the IDs of people in my "friends" window (this might not be the most efficient way to accomplish this specific task, but since I'd like to know how to do this in general it's a good example).
This means that if I have more than a small number of friends I have to scroll down for Facebook to add them to the page.
I've added logic that scrolls the page down to the footer, but I don't know how to force my function that grabs the IDs to run after the content loads.
For now, I've resorted to using setTimeout for a few seconds - obviously, this isn't guaranteed to at the appropriate time, so I'd like to know how to do this properly:
var k;
function doit(){
k = document.getElementsByClassName("_698");
var g= Array.prototype.slice.call(k);
confirm(g.length);
// the confirm is just to make sure it's working
// (if i don't use setTimeout it'll return a smaller number
// since not all the friends were included)
}
window.addEventListener("load", function(){
document.getElementById( "pageFooter" )
.scrollIntoView();setTimeout(doit,3000);
});
Crayon Violent details how to accomplish this in his answer to JavaScript detect an AJAX event. The trick is to hook the underlying XMLHttpRequest object in order to detect when a request is sent.
I've re-written the logic there a bit to make it more suitable for your needs:
//
// Hooks XMLHttpRequest to log all AJAX requests.
// Override ajaxHook.requestCompleted() to do something specific
// in response to a given request.
//
var ajaxHook = (function()
{
// we're using a self-executing function here to avoid polluting the global
// namespace. The hook object is returned to expose just the properties
// needed by client code.
var hook = {
// by default, just logs all requests to the console.
// Can be overridden to do something more interesting.
requestCompleted: function(xmlHttp, url, method) { console.log(url); }
};
// hook open() to store URL and method
var oldOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(method, url)
{
this.hook_method = method;
this.hook_url = url;
oldOpen.apply(this, arguments);
}
// hook send() to allow hooking onreadystatechange
var oldSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function()
{
var xmlhttp = this;
//hook onreadystatechange event to allow processing results
var oldReadyStateChange = xmlhttp.onreadystatechange;
xmlhttp.onreadystatechange = function()
{
oldReadyStateChange.apply(xmlhttp, arguments);
if ( this.readyState === 4 ) // completed
{
hook.requestCompleted(xmlhttp,
xmlhttp.hook_url, xmlhttp.hook_method);
}
};
oldSend.apply(this, arguments);
};
return hook;
})();
With this bit of code loaded in your userscript, you can then implement your logic as follows:
var k;
function doit()
{
k = document.getElementsByClassName("_698");
var g= Array.prototype.slice.call(k);
confirm(g.length);
}
window.addEventListener("load", function()
{
ajaxHook.requestCompleted = function(xmlhttp, url, method)
{
// is this the request we're interested in?
// (Facebook appears to load friends from a URL that contains this string)
if ( /AllFriendsAppCollectionPagelet/.test(url) )
{
// Facebook defers rendering the results here,
// so we just queue up scraping them until afterwards
setTimeout(doit, 0);
}
};
// trigger loading of more friends by scrolling the bottom into view
document.getElementById( "pageFooter" )
.scrollIntoView();
});

JS difference between enter page and refresh page

Is it possible to detect situation when page is entered and when the same page is refreshed
if (entered) alert("hi");
if (refreshed) alert("you've refreshed");
Somehow there are some little differences between page rendering when entered and when refreshed and it would be much easier to detect the case than to debug it for me (if its even possible - maybe some browser optimization stuff is causing it).
This isn't an ideal solution, but if your page can load in under 5 seconds than this will work, and assuming you are not navigation to another page, then returning within 5 seconds.
window.onbeforeunload = function(){
window.sessionStorage.setItem('lastvisit', new Date().getTime());
}
var lastVisit = +window.sessionStorage.getItem('lastvisit');
var isRefresh = (new Date().getTime() - lastVisit) < 5000;
console.log(isRefresh);
There is no perfect way of tracking reloads verses new page loads but this solution works in most situations. Use sessionStorage in combination with an unload event:
(function (win) {
'use strict';
var reloaded = false,
ss = win.sessionStorage,
offset = 1000, // 1 second, may need tweaking if
// your page takes a long time to load/where
// this code is located in your page
now = function () {
return (new Date()).getTime();
},
lastUnload = ss.getItem('lastunload'),
loadStatus = document.getElementById('status');
// sessionStorage returns null if nothing was stored
if (lastUnload !== null) {
// sessionStorage returns a string, +lastUnload
// coerces the string held in lastUnload into an integer
// see https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/Arithmetic_Operators#-_.28Unary_Negation.29
if (+lastUnload + offset > now()) {
reloaded = true;
}
}
win.addEventListener('unload', function () {
ss.setItem('lastunload', now());
}, false);
if (lastUnload === null) {
loadStatus.innerHTML = 'First visit of session.';
} else if (reloaded) {
loadStatus.innerHTML = 'Page was reloaded.';
} else {
loadStatus.innerHTML = 'Navigated back to page after leaving';
}
}(window));
This code defines a page reload as returning to the page within 1 second of leaving it, so there could be false positives if someone leaves the page and immediately hits the back button but with normal browsing behavior that really shouldn't happen. You can modify the offset variable if you want to give more or less leeway, but 1 second seems to be a good default.
After developing this code I also found this similar answer.
If sessionStorage is available, you can use that.
if (!window.sessionStorage.getItem('visited')) {
//entered
window.sessionStorage.setItem('visited', true);
}
else {
//refreshed
}
More on sessionStorage

How to make a cache/memorizer in BackboneModel

Let's suppose I have a View which can make model.fetch() and then a request to the server.
I would like to implement:
1) A checker able to memorise the result
2) refresh the result (making the request to the server) only if the last request to the server is older than ten minutes.
What should I do?
Is there already a piece of code to make that?
define([], function() {
var MyModel = Backbone.Model.extend({
url: function () {
return "http://localhost/restapi/model/";
},
fetch () {
if(diffTime > 10minutes) {
// make request to the server
}
else {
// return memo
}
}
});
});
You need to override the Backbone.sync method http://documentcloud.github.com/backbone/#Sync.
This code does the saving to local storage to implement a cache http://documentcloud.github.com/backbone/docs/backbone-localstorage.html.
It is fairly simple to add some logic in the "read" case to fetch from the server if the data is older than 10 minutes.
As codemonkey said, localstorage would be a good option. But if you don't want to use a library for that, you can use this class to extend those models who require the cache functionality.
var CachedModel = Backbone.Model.extend({
lastFetch: null, // millisec.
cache: { }
fetch: function () {
if(!this.lastFetch || (lastFetch - Date.now() > 10*60*1000) {
// make request to the server
}
else {
// return this.cache
}
}
});
I have found https://github.com/Ask11/backbone.offline to work really well for me.
The only vice is that it uses localStorage, you could also opt for more support by going with rewriting bits and pieces for use with amplify.store http://amplifyjs.com/api/store/.

Rate limiting to prevent malicious behavior in ExpressJS

Someone made me aware of some flaws in an application I'm working on (mostly within my JavaScript on the front-end), that leaves open the possibility of, say, clicking a ton of buttons at once and sending out a ton of transactional emails. This is clearly not good.
I think one way to handle this in ExpressJS is by using app.all() to count the number of requests that happen within a certain timeframe. I'd store this in the session metadata with timestamps, and if more than X requests happen in Y time, I cut them off for awhile until the limit expires.
Has anyone done this before or have any tips/hints to help me out? Something that's easy to drop in and out of my app is preferable. Thanks!
You could use the Collate object in your webpage.
function Collate(timeout) {
this.timeout = timeout || 1000;
}
Collate.prototype = {
time: 0,
idle: function() {
var t = new Date().getTime();
return (t - this.time > this.timeout && (this.time = t));
},
prefer: function(func) {
this.func = func;
clearTimeout(this.timer);
this.timer = setTimeout(func, this.timeout);
}
};
If you want a function to run once and not run again within the next 1 second.
Like if you want to prevent the user from submitting a form many times, you do this:
var timer = new Collate(3000); //3 seconds
button1.onclick = function() {
if(timer.idle()) {
button1.form.submit();
} else alert("Don't click too quickly!");
}
//or on the form tag
<script>var submitTimer = new Collate(3000);</script>
<form action="post" onsubmit="return submitTimer.idle();">
If you expect an event to fire multiple times and only want to react to the last time it fires.
Like if you want to search after a user has finished typing, you do this:
var timer = new Collate(700); //0.7 seconds
textfield1.onkeyup = function() {
timer.prefer(function() {
autocomplete.search(textfield1.value);
});
};

Categories

Resources