Aborting $.post() / responsive search results - javascript

I have the following kludgey code;
HTML
<input type="search" id="search_box" />
<div id="search_results"></div>
JS
var search_timeout,
search_xhr;
$("#search_box").bind("textchange", function(){
clearTimeout(search_timeout); search_xhr.abort();
search_term = $(this).val();
search_results = $("#search_results");
if(search_term == "") {
if(search_results.is(":visible"))
search_results.stop().hide("blind", 200);
} else {
if(search_results.is(":hidden"))
search_results.stop().show("blind", 200);
}
search_timeout = setTimeout(function () {
search_xhr = $.post("search.php", {
q: search_term
}, function(data){
search_results.html(data);
});
}, 100);
});
(uses the textchange plugin by Zurb)
The problem I had with my original more simple code was that it was horribly unresponsive. Results would appear seconds later, especially when typed slower, or when Backspace was used, etc.
I made all this, and the situation isn't much better. Requests pile up.
My original intention is to use .abort() to cancel out whatever previous request is still running as the textchange event is fired again (as per 446594). This doesn't work, as I get repeated errors like this in console;
Uncaught TypeError: Cannot call method 'abort' of undefined
How can I make .abort() work in my case?
Furthermore, is this approach the best way to fetch 'realtime' search results? Much like Facebook's search bar, which gives results as the user types, and seems to be very quick on its feet.

You'd do well to put a small delay in before sending the request. If the user hits another key within 100ms (or some other time of your choosing) of the last there is no need to send the request in the first place.
When actually sending the request you should check to see if one is already if active. If it is, cancel it.
e.g.
if (search_xhr) {
search_xhr.abort();
}
don't forget to reset that var on a successful retrieval. e.g. delete search_xhr;

Related

How to handle multiple requests being sent in JavaScript?

Working on a platform, to enable auto-ticketing functionality. For which a REST API request is used for ticket creation. Unfortunately, there are 2 requests popping simultaneously, which results in creating duplicated tickets.
How to handle such case and send only one of these requests?
Tried adding the 2nd request in the response callback of the first, though this does not seem to work.
if (flag == 1){
logger.debug("Node-down alarm-Request raised - +sitn_id);
clearTimeout(mouseoverTimer);
mouseoverTimer = setTimeout(function(){
logger.debug("Inside Call back function - ");
//function call for ticket creation
incidentRequest(sitn_id,confUtil.config.mule_url);
}, 10);
You really should show more of the code that makes the request, though it seems as if you are doing some ajax inside your 'incidentRequest', so I will presume that (if that isn't what you are doing, then please, show your code....) - and since you tags say javascript and jquery - well, here goes...
To stop the 'double send' in an AJAX call, it is simple:
function incidentRequest(sitn_id,confUtil.config.mule_url){
// stop the double by clearing the cache
$.ajaxSetup({cache: false});
// continue on with the AJAX call
// presuming the url you want is confUtil.config.mule_url
// and the data you want to send is sitn_id
$.post(confUtil.config.mule_url, 'sitn_id=' + sitn_id, function (data) {
// do cool stuff
});
}
Hopefully that will help you get moving. If not, then we will need more code of what is going on around all this.

Update output from user input and asynchronous server-side API calls

I am developping a web application.
At some point, I have an input field where the user can enter a number. Right next to it, I would like to show as an output the double of that number. Of course, that can be done easily client-side using Javascript in the following way (jQuery syntax):
$(document).on("change", "#input", function () {
var x = $("#input").val();
var y = 2 * x;
$("#output").val(y);
});
However, for various reasons linked with the real application, I need to perform the computation server-side and show the result client-side after calling my API. My naive approach was to implement it in the following way (jQuery syntax):
$(document).on("change", "#input", function () {
$("#output").val("Currently computing result");
$.ajax({ ... }).done(function (result) {
$("#ouput").val(result);
});
});
Each time the user updates the field, an event gets triggered and an asynchronous AJAX query is sent. Each time a result comes back, the output field is updated. However, if the network is slow, or the server-side computations are multi-threaded and take random time, there is no guarantee that the results come back in the same order as the successive changes made by the user. In the end, it can happen that an inconsistent result is shown.
I expect that this is a typical situation. How can I solve it? Should I be using a front-end framework like Vue.js (despite the fact that I still want to host the computation server-side)? Do front-end frameworks generally handle this difficulty? Is there a simple way to handle it using plain Javascript? Thanks!
You can put loader to prevent user actions(ajax calls) until the response of current ajax call will not be come:
$(document).on("change", "#input", function () {
// Show loader
$("#output").val("Currently computing result");
$.ajax({ ... }).done(function (result) {
$("#ouput").val(result);
// Hide loader
});
});

One entry point for all ajax callbacks for a particular call in javascript

When communicating with a server in javascript in my single page browser application, I would like to provide a callback function that is always called after the server replies, regardless of whether the result was a success or some kind of error.
Two cases where I need this:
1) I want to disable a "save" button while waiting for the server's response, and enable it again after the server responds with an error or a success.
2) I have a polling mechanism where I want to prevent stacking of calls when the server for some reason is being slow to respond - I want to wait for one poll call to finish before making the next.
One solution I have right now involves making sure that two functions (success and error) get passed along as options in a long method chain, which feels like a fragile and cumbersome solution (pseudo-ish code):
function doCall() {
framework1.callit({success : myCallback, error : myCallback})
};
framework123.callit = function(options) {
options = options || {};
if (options.error) {
var oldError = options.error;
options.error = function(errorStuff) {
// callit error stuff
oldError(errorStuff);
} else {
// callit error stuff
}
generalCallFunction(options);
}
function generalCallFunction(options) {
options = // ... checking success and error once again to get general success and error stuff in there, plus adding more options
ajax( blah, blah, options);
}
I also have a backbone solution where I listen to the sync event plus an error callback, in similar ways as above.
I'm always scared that error or success functions get lost on the way, and the whole thing is hard to follow.
Any framework or pattern for making this stuff as easy as possible? Is it a weird thing to have general things that should always happen whether the result was an error or a success?
You can use jQuery.ajax({ details here... ).always(callback);
Or, in Backbone
// logic to create model here
model.fetch().always(callback);

complex problem with setTimeout, clearTimeout, and javascript

I am a bit of a noob at Javascript and am writing an "autoresult" script that automatically gets the results of an as the user is typing. However, because the PHP backend is slow, I want the script to check and see if it has been 1 second since the last keyup. That way, the PHP backend won't be called unless the user is done typing. My idea was to use setTimeout and clearTimeout to handle this. However, my script doesn't work. Here is the input that calls the script:
<input type="text" id="inputbox" onkeyup="lookup_input(this.value,0);" />
"0" is a flag used to check whether a Timeout has been set. Here is the script:
var timeOut1;
function showQuery(input_myinput2) {
$.post("mybackendfile.php", {queryString: input_myinput2}, function(data){
if(data.length >0) {
$('#mydiv').html(data); //php backend stuff, don't worry about this
}
});
}
function lookup_input(input_myinput,flag) {
if(input_myinput.length == 0) {
$('#mydiv').hide(); //check to see if there is an input, and if not, hide the div that displays autoresults
}
else {
//the flag checks to see if the Timeout has been set
if(!flag) {
timeOut1 = setTimeout(function(){showQuery(input_myinput)}, 1000);
//change the flag to "1" so that if another key is pressed it will throw the else statement, and if the key is pressed soon enough, it will clear the Timeout
$('#inputbox').onkeyup('lookup_input(this.value,1)');
$('#mydiv').show();
$('#mydiv').html('Searching... ');
}
else { //if timeout has been set then and next key has been pressed
clearTimeout(timeOut1);
$('#mydiv').html('Searching... ');
timeOut1 = setTimeout(function(){showQuery(input_myinput)}, 1000);
}
}
}
any suggestions on how to access the showQuery function correctly and how to get this script to work? also, any suggestions on another way to do the autoresult stall besides using setTimeout/clearTimeout? thanks!
Underscore.js provides a neat debounce function that abstracts away this (often needed) feature.
Take a peek at the annotated source code on how it is implemented.
You really don't need the flag. The event handler should always clear the timeout and start a new one.
The way you've got it now, you're adding another, redundant event handler for the input element.
I also think you should move that code that shows the "Searching..." message to inside the timeout code, because it'd be a little misleading to show it 1 second before you actually do start searching.

"Waiting" for ReadyState to be 4 before returning a value

I have looked in lots of places for this, and I'm probably being an idiot because this is done a lot, but here's my situation.
I am trying to display a checkbox next to an e-mail field on my website iff the e-mail has not been used to register already.
What I have is something like this:
$('[name=reg_email]').change( function() {
if(!emailUsed()) {
//Update image to be a green checkmark
} else {
//Update image to be a huge red X
}
}
And my "emailUsed" function should be returning a Javascript boolean variable depending on whether or not the e-mail address is in the database. To facilitate this, I've created a script which will determine if it's there or not. So the emailUsed() function just needs to call the script and return, but I need to wait until readystate == 4 before I return, and all of the methods I have found for waiting for readystate to equal 4 prevent me from returning a value at all:
function emailUsed() {
var req = $.get('scripts/email_used.php?email='+$('[name=reg_email]').val());
//Wait for req.readystate to be 4
return req.responseText == 'True';
}
But nowhere can I find something that explains how to do this and still return a value. Everything seems to use callback functions and this and that, but I can't get THIS method to return a value.
Help, please!
Doing a busy wait for req.readyState === 4 is considered bad design practice. You're tying up the UI thread and preventing the browser from updating. If the server is slow enough to respond, the user will get prompted whether to cancel further processing.
If you take a look at $.get(), it takes a completion function as one of its arguments. You should perform your success/failure logic in this function. You can do this by disabling your Submit button until you get back a success message.
You're missing the point of asynchronous methods. The main point is that there is some calculation to be done, and you don't want to tie up the current thread waiting for that calculation. Therefore, long running methods should not return a value, but instead they should provide a callback that will be passed the status of the call, without making the entire app wait.
I would suggest the following
function emailUsed (callback) {
var req = $.get('scripts/email_used.php?email='+$('[name=reg_email]').val(),
function(data) {
callback(data == 'True');
}
);
}
$('[name=reg_email]').change( function() {
emailUsed(function(isUsed){
if (isUsed) {
//Update image to be a green checkmark
} else {
//Update image to be a huge red X
}
});
}

Categories

Resources