jQuery.ajax(): discard slow requests - ajax

I've build a livesearch with the jQuery.ajax() method. On every keyup events it receives new result data from the server.
The problem is, when I'm typing very fast, e.g. "foobar" and the GET request of "fooba" requires more time than the "foobar" request, the results of "fooba" are shown.
To handle this with the timeout parameter is impossible, I think.
Has anyone an idea how to solve this?

You can store and .abort() the last request when starting a new one, like this:
var curSearch;
$("#myInput").keyup(function() {
if(curSearch) curSearch.abort(); //cancel previous search
curSearch = $.ajax({ ...ajax options... }); //start a new one, save a reference
});
The $.ajax() method returns the XmlHttpRequest object, so just hang onto it, and when you start the next search, abort the previous one.

Assign a unique, incrementing ID to each request, and only show them in incrementing order. Something like this:
var counter = 0, lastCounter = 0;
function doAjax() {
++counter;
jQuery.ajax(url, function (result) {
if (counter < lastCounter)
return;
lastCounter = counter;
processResult(result);
});
}

You should only start the search when the user hasn't typed anything for a while (500ms or so). This would prevent the problem you're having.
An excellent jQuery plugin which does just that is delayedObserver:
http://code.google.com/p/jquery-utils/wiki/DelayedObserver

Make it so each cancels the last. That might be too much cancellation, but when typing slows, it will trigger.

That seems like an intense amount of traffic to send an ajax request for every KeyUp event. You should wait for the user to stop typing - presumably that they are done, for at least a few 100 milliseconds.
What I would do is this:
var ajaxTimeout;
function doAjax() {
//Your actual ajax request code
}
function keyUpHandler() {
if (ajaxTimeout !== undefined)
clearTimeout(ajaxTimeout);
ajaxTimeout = setTimeout(doAjax, 200);
}
You may have to play with the actual timeout time, but this way works very well and does not require any other plugins.
Edit:
If you need to pass in parameters, create an inline function (closure).
...
var fun = function() { doAjax(params...) };
ajaxTimeout = setTimeout(fun, 200);

You will want some kind of an ajax queue such as:
http://plugins.jquery.com/project/ajaxqueue
or http://www.protofunc.com/scripts/jquery/ajaxManager/
EDIT:Another option, study the Autocomplete plug-in code and emulate that.(there are several Autocomplete as well as the one in jquery UI
OR just implement the Autocomplete if that serves your needs

Related

Elegant Protractor Logging on browser.wait()

For some time now, our team has been using the Protractor/Jasmine combo in order to do E2E testing and it has worked out great for us.
Recently, I've been assigned the task of improving the logging and have noticed 2 areas in our code where the logging could be improved.
One of these areas is with using browser.wait(). We're currently using the method in the form of browser.wait(condition, timeOut) and excluding the third parameter which is a message to be written to the console in case of a failure.
I decided to comb the code and insert a message into each of these methods and the result has been OK. Although the code works, I'm wondering if there is a more elegant way of doing this.
I'm currently saving the XPath of whatever element I'm waiting on and then if that fails, displaying a message in the form: getWait() timeout due to <element_name> { XPath = <element_XPath> } being not visible or enabled such that you can click it. The pattern changes if we're using isPresent(), elementToBeClickable(), visibilityOf(), etc.
Is there a way of getting the current context of the code I'm executing when browser.wait() is executed? Can I instead display the element or more information on what caused the timeOut?
Let me know if I can clarify further. Thanks
Even I came faced the same problem when using browser.wait where it won't show any detailed log on time out. So what I have done is, I created a wrapper class for waitHandling and added failure messages based on the wait type. Kindly have a look at below code.
var browserWaitHandler = function () {
var expectedConditions = protractor.ExpectedConditions;
var defaultWaitTime = 5000;
this.waitForElementPresent = function (_element,customWaitTime) {
return browserWait(expectedConditions.presenceOf(_element),customWaitTime,"Wait timeout after waiting for element to be Present with locator "+_element.locator().toString());
};
this.waitForElementVisible = function (_element,customWaitTime) {
return browserWait(expectedConditions.visibilityOf(_element),customWaitTime,"Wait timeout after waiting for element to be Visible with locator "+_element.locator().toString());
};
this.waitForElementClickable = function (_element,customWaitTime) {
return browserWait(expectedConditions.elementToBeClickable(_element),customWaitTime,"Wait timeout after waiting for element to be clickable with locator "+_element.locator().toString())
};
this.waitForElementContainsText = function (_element,expectedText,customWaitTime) {
return browserWait(expectedConditions.textToBePresentInElement(_element,expectedText),customWaitTime,"Wait timeout after waiting for element to Contain text as "+expectedText+" with locator "+_element.locator().toString())
};
var browserWait = function (waitCondition,customWaitTime,timeoutMessage) {
return browser.wait(waitCondition,customWaitTime | defaultWaitTime,timeoutMessage);
};
};
And also in protractor we have a method called locator() which can be used with ElementFinder and ElementArrayFinder objects to get the locator that is used to find the element.Refer below example code,
var loginButton = element(by.buttonText("Login"));
console.log("Locator used for login button is:"+loginButton.locator().toString());
*OUTPUT:*
Locator used for login button is:by.buttonText("Login")

React dispatcher WAITFOR

I'm trying to use the waitFor function of react.js but it seems I'm doing something wrong.
What I want to do i basic, wait for a store to be filled before calling it from another store.
1.Register token in the first store
RipplelinesStore.dispatcherIndex= Dispatcher.register(function(payload) {
var action = payload.action;
var result;
switch(action.actionType) {
case Constants.ActionTypes.ASK_RIPPLELINES:
registerAccount(action.result);
RipplelinesStore.emitChange(action.result);
break;
}
});
2.Write the wait for in the other store
Dispatcher.register(function(payload) {
var action = payload.action;
var result;
switch(action.actionType) {
case Constants.ActionTypes.ASK_RIPPLEACCOUNTOVERVIEW:
console.log("overviewstore",payload);
Dispatcher.waitFor([
RipplelinesStore.dispatcherIndex,
]);
RippleaccountoverviewsStore.test= RipplelinesStore.getAll();
console.log(RippleaccountoverviewsStore.test);
break;
}
return true;
});
Unfortunately my getall() method return an empty object (getAll() is well written). So it seems that the waitFor dispatcher function is not working.
Basically I know that's because the first store is still receiving the answer from the server but I thought that waitFor would waitfor it to be fetched I don't get it.
Any clue ? Thanks!
Edit: I fire the first store fetch like tha. What I don't understand is that I'm dispatching the load once my backbone collection has fetched (I dispatch on succeed with a promise...)
ripplelinescollection.createLinesList(toresolve.toJSON()).then(function() {
Dispatcher.handleViewAction({
actionType: Constants.ActionTypes.ASK_RIPPLELINES,
result: ripplelinescollection
});
});
I also tried to bind the waitfor to an action which is never called but the other store is still not waiting ! WEIRD !
seems like the problem is the async fetch from the server. waitFor isn't supposed to work this way. You will have to introduce another action that is triggered as soon as the data has been received from the server.
Have a look at this answer: https://stackoverflow.com/a/27797444/1717588

Make jQuery ajax calls in order

I want to make a stack of Ajax calls in this way: call(n) starts after call(n-1) finished...
I cannot use async:false for many reasons:
some requests maybe jsonp (the most relevant)
I have other ajax requests that may work meanwhile..
The browser got blocked
I cannot chain my requests this way:
$.post('server.php', {param:'param1'}, function(data){
//process data
$.post('server.php', {param:'param2'}, function(data){
//process data
});
});
Because the number and params of the requests are dynamically created from user input.
A small example that illustrates my problem.
You will see that the server response order is random, what I want to achieve is to have it in order
Response to arg1
Response to arg2
Response to arg3
Response to arg4
Response to arg5
Response to arg6
Any help would be very appreciated, thanks.
Ok, jQuery Ajax returns a Deferred Object, this can help you achieve this.
Here is how to do it:
var args = ['arg1','arg2','arg3','arg4','arg5','arg6'];
deferredPost(0, 5);
function deferredPost(index, max){
var delay = Math.random()*3;
if (index<max){
return $.post('/echo/html/', {html:('Response to '+args[index]), delay:delay},
function(data){
$('#response').append(data+'<br>');
}).then(function(){
deferredPost(index+1, max);
});
} else {
return $.post('/echo/html/', {html:('Response to '+args[index]), delay:delay},
function(data){
$('#response').append(data+'<br>');
});
}
}
DEMO
Here I used then function.
I also recommend to read a little bit more about deferred objects, they can solve a couple of common problems.
This is a job for a queue.
var queue = ['arg1','arg2','arg3','arg4','arg5','arg6'];
function runQueueInOrder() {
if (queue.length === 0) { return; }
var arg = queue.pop();
var delay = Math.random()*3;
$.post('/echo/html/', {html:('Response to '+ arg), delay:delay},
function(data){
$('#response').append(data+'<br>');
}).then(function() {
runQueueInOrder();
});
}
runQueueInOrder();
You don't need to use jQuery's then for this to work if you've encapsulated the processing of the queue in a function. It's handy though. The code is destructive as it removes elements from the original array (but as they are processed, it's usually OK).
The method runQueueInOrder is called to initiate processing.
When there is no more work to be done, the function simply exits. (I've written a version that polls on a timer before, but that's not needed here).
The function grabs the next work arg, calls your post call syntax, and when done uses jQuery's deferred then callback to call the function again (to process the queue further if needed).
(I looked at the other answer and found it confusing to follow, so I took a simpler approach. Using my simple version, you can add new items as new work is discovered--or remove them.).

How do I properly handle delayed operations in Node.JS?

I'm new to ASYNC programming so please bear with me. I have a call to a web service API that can be unpredictably slow. On the front end, I can handle it with a "loading" lightbox or something. However, on the backend, I have my request:
var req = http.request( options, function(res) {
res.on('data', function(chunk) {
doStuff();
} );
res.on('end', function() {
doMoreStuff(); // This can take a while to get to.
return someInfo();
} );
} );
req.end();
All of this is in a makeRequest module. So should I pass my callback function into makeRequest and then have it run after the 'end' event? It seems like this can lead to a very long chained event structure.
So any help on how to structure this would be greatly appreciated.
note: the above is mostly pseudocode so if there are syntax errors, please understand that it's pseudocode
Yes, generally you would pass a callback into whatever function you have this in, and when 'end' is emitted, you should take the data that you collected in the request, and pass it to your callback.
I realize it's pseudocode and you may know, I just want to say it anyways. Remember that 'data' can be called more than once, and that 'return' in your end function won't do anything.
For an example of doing a request, you can look at my answer over here.
Why won't my ExpressJS properly execute a request command?

Calling multiple views in CouchApp query

I need to search the CouchDB based on several criteria entered in a form. Name, an array of Tags and so on. I would then need various views to index on these fields. Ultimately, all the results will be collated in data.js and provided to mustache.html. Say there are 3 views - docsByName, docsByTags, docsById.
What I don't know is, how to query all these views in query.js. Can this be done and how ?
Or should the approach be of that to write one view that makes multiple emits for each search somehow ?
Thank you.
From what you say I assume you are using Evently, so I will quote from Evently primer:
The async function is the main star, which in this case makes an Ajax request (but it can do anything it wants). Another important thing to note is that the first argument to the async function is a callback which you use to tell Evently when you are done with your asynchronous action. [...] Whatever you pass to the callback function then becomes the first item passed to the data function.
In short: put your Ajax requests in async.js.
As a side note: Evently is only one of the possible choices to write a couchapp and it is not clear if it is maintained. However it works and it is easy to rearrange the code to not use it.
EDIT: here is a sample async function (cut&paste from an old program):
function(cb, e) {
var app = $$(this).app
;
app.db.openDoc('SOMEDOCID', {
error: function(code, error, reason) {
alert("Error("+code+" "+error+"): "+reason);
}
, success: function(doc) {
app.view('SOMEVIEWNAME', {
include_docs: true
, error: function(code, error, reason) {
alert("Error("+code+" "+error+"): "+reason);
}
, success: function(resp) {
resp.doc = doc;
cb(resp);
}
});
}
});
}

Resources