JS onclick, override previous click - javascript

I am writing a query engine where the user enters parameters and then clicks query.
The ng-click event can take up to 7-10 seconds, but rendering of page 1 takes less than a second. If a user wants to re-submit a new query before that time is up there is a bit of a lag because I believe the original function call is still returning.
Is there a way to abruptly stop any previous function calls and only call the most recent?
Below is the query function and the call from the HTML
$scope.query = function(name, matchMode, domain) {
name = name.trim();
$scope.pageNumber = 0;
start = 0
end = $scope.perPage;
$http.get(ROOT_API+"query/asset/"+name+"/"+matchMode+"/"+domain).success(
function(data){
$scope.results=data;
$scope.displayResults=$scope.results.slice(start,end);
$scope.totalResults=data.length;
$scope.totalPages=Math.floor($scope.totalResults / $scope.perPage);
setPageValues($scope.displayResults)
});
setTimeout(function() {
$scope.displayResults.concat(setPageValues($scope.results.slice(end,$scope.totalResults)))
},2000);
}
<input ng-model="queryName" placeholder="name">
<input ng-model="matchMode" placeholder="match mode" defaultValue="ANYWHERE">
<input ng-model="domain" placeholder="domain" defaultValue="ANY">
<button ng-click="query(queryName, matchMode, domain)">QUERY</button>

Your code has gone in multiple threading,
first thread has been assigned to code inside you click function from where it has been started and the second one assigned to http get request. so first thread gets executed as it ignores next thread execution.
For that sake in javascript, we have callback functions for handling the async calls.
instead of using setTimeout function, record the API call response in callback.
let apicallFun = function(requestObj, Callback){
console.log("Inside API call");
return $http.get('/your_URL', requestObj)
.then(function(response){
Callback(response);
})
}
and then inside your click function call this apicallFun as:
$scope.clickFun = function(requestObj){
apicallFun(requestObj, function(response){
//according to your response object write the rest of your code.
}
}

Maybe you could wrap the whole thing in a do..while loop and have some exit variable you can set to true?

Related

jQuery buttons selector wait ajax finished php exec command using async-await not working

I have a php page with multiple forms that are sending an exec command to the server:
Form 0, button RunAll -> It must run all the following button commands sequentially with jQuery class selector loop
Form 1, button 1 -> Exec Apache shell cmd 1
Form 2, button 2 -> Exec Apache shell cmd 2
Form3, button 3 -> Exec Apache shell cmd 3
I want that RunAll button runs all the requests in ajax async = true to do not freeze the page but sequentially one after the other.
The issue is that the jQuery loop clicks all the buttons together and I am not able to wait (using async-await) that the first ajax call is finished before it clicks the second button and then so on for the third button.
Please help me.
I have tried
1- global variable in in ajax success:
2- Promises
3- Async-await
The Apache runs out of memory because of all processes are running at the same time
function submitMultiForm(){
$('.btn-info').each(function() {
asyncClick($(this));
});
}
async function asyncClick(myBttObj)
{
//GET THE SPECIFIC FORM AND TEXT AREA
var myForm = myBttObj.parent();//alert(myForm.attr('id'));
var myFormAreaText = myBttObj.siblings('.myanswer');//alert(myFormAreaText.attr('id'));
//SET icons
var mySpinIcon = myBttObj.siblings('.fa-spin');
var mySuccIcon = myBttObj.siblings('.fa-check');
var myErrIcon = myBttObj.siblings('.fa-times');
var myErrAjxIcon = myBttObj.siblings('.fa-exclamation-triangle');
var url = myForm.attr("action");
var serializedData = myForm.serialize()+'&'+$('#fparam').serialize();
try {
const res = await doAjax(url, serializedData, mySpinIcon, mySuccIcon, myErrIcon,myErrAjxIcon);
//write always the log
var myAreaID = myFormAreaText.attr('id');
//var filtered_data = res.html();
myFormAreaText.append( res ); //I added exit to the if isset($_POST) to not giving back the whole page
//(because the url is the same page of the origin of the ajax request)
//brwsr console
console.log(myAreaID)+':';
console.log(res);
//blur off button
myBttObj.blur();
} catch(err) {
console.log(err);
}
}
function doAjax(url, serializedData, mySpinIcon, mySuccIcon, myErrIcon,myErrAjxIcon)
{
let result;
try {
result = $.ajax({ url:url,
type:"post",
data: serializedData,
datatype: 'html',
beforeSend: function() {mySpinIcon.show();mySuccIcon.hide();myErrIcon.hide();myErrAjxIcon.hide()},
success: function(data) {
if ( data.toLowerCase().indexOf("error") >-1 ) {
mySpinIcon.hide();mySuccIcon.hide();myErrIcon.show();myErrAjxIcon.hide();//error from the shell
} else {
mySpinIcon.hide();mySuccIcon.show();myErrIcon.hide();myErrAjxIcon.hide();
}
},
error: function() {mySpinIcon.hide();mySuccIcon.hide();myErrIcon.hide();myErrAjxIcon.show()}
});
return result;
} catch (error) {
console.error(error);
}
}
Your attempted solution seems to be based on a misunderstanding of how the await keyword pauses the execution of an async function. The key insight here is that only the execution of your async function is paused, not the execution of the rest of your script, such as the function calling the async functions. See MDN on await for a good explanation, in particular this paragraph (emphasis mine):
An await can split execution flow, allowing the caller of the await's function to resume execution before the deferred continuation of the await's function. After the await defers the continuation of its function, if this is the first await executed by the function, immediate execution also continues by returning to the function's caller a pending Promise for the completion of the await's function and resuming execution of that caller.
Note that you are calling your async functions synchronously in your event handler for .btn-info, resulting in the following execution order:
asyncClick runs for the first button ("A1"), its execution is suspended while it awaits doAjax ("D1") to yield a result
asyncClick runs for the second button ("A2"), its execution is suspended while it awaits doAjax ("D2") to yield a result
(other asyncClick calls are executed as above)
(some time passes)
when D1 yields, A1 continues execution
and so on...
This is what causes your server to start processing everything concurrently.
Instead, you need to explicitly add logic that only triggers the next async call when the previous one has returned. There are many ways to do this, with async/await as well as plain Promise objects.
Additional notes:
If you litter your code with some console.log() statements, you will be able to inspect the order in which your functions are executed. (Your logging points of interest would be when functions are entered, and when execution continues after an await statement.)
the keyword you seem to be looking for is serialization (to describe waiting for the completion of a command before the next), you should be able to find good resources if you add this term to your searches.
On a higher level note, this sentence:
The Apache runs out of memory because of all processes are running at the same time
suggests that you might have an architectural problem, since there is actually no semantical need to run these commands serialized, it's only an implementation- or hardware-based limitation that forces you to do this. (ie. it is not that CMD2 does not make sense until CMD1 was run, it's just that your server is unable to run both at the same time due to low memory.)
Ideally, your application frontend would not need to know about these issues at all, it should just be able to wait until the server returns for each command -- whether the server is able to work on these commands concurrently or not, is up to the discretion of the server.
However, these higher level questions about code organization would be better suited for other sites of the network, such as softwareengineering or codereview.

Controlling an $.ajax function within a js "for" loop?

Here's my issue. I have a js function that performs an $.ajax call to fetch some data from a server. When it gets that data back, I need to pass control back to the browser in order to show an update to a div.
The js function is itself within a for loop, and I need to ensure that the for loop does not advance until the js function has updated the div and allowed the Browser to display that update, at which point the for loop advances and the js function (with its ajax call) is called again, continuing until the for loop test causes the loop to end.
I've tried many different approaches - callbacks, promises etc, but to date I can't seem to get a handle on ensuring that the loop doesn't advance until the js function gets its server data, updates the div, causes the browser to display that update and fully completes.
Here's a simple stripped-down version of the function:
function myFunction (email) {
var request = $.ajax( {
url: 'getit.php',
cache: false,
async: false,
method: "post",
timeout: 1000,
data: "requesttype=getemailname&email="+encodeURIComponent(email)
});
request.done(function(response) {
$("#myDiv").html(response);
});
}
and here's part of the js that calls it:
.....
var emailscount = emails.length;
for(var i=0;i<emailscount;i++) {
myFunction (emails[i]);
}
.....
So, my issues are:
1) myFunction must allow the browser to display the updated div html - I'm not sure how to achieve that?
2) the for loop should only proceed when myFunction has received the data back from the server, updated the div html, AND allowed the browser to display that div.
At the moment, I have set the $.ajax call async flag set to "false" to stop execution until the data comes back, but how do I ensure the browser displays the new div content, and that the for loop does not proceed to call myFunction again until the previous myFunction call fully completes?
Any help you can give me would be very welcome, as right now I can't get this all to work!
Sounds like you need a recursive function, not a for loop with synchronous ajax calls
(function myFunction(i) {
$.ajax({
url: 'getit.php',
method: "post",
timeout: 1000,
data: {
requesttype : 'getemailname',
email : emails[i]
}
}).done(function(response) {
$("#myDiv").html(response);
if (emails[++i]) myFunction(i); // continue when this one is done
});
})(0);
Thanks for everyone's help! I'm making good progress (including taking care of JQuery deprecations!) but have run into a further problem. As I need to hand control back to the browser in order to show the refreshed div as I recurse, I'm calling a setTimeout as follows:
var nextBitOfWork = function () {
return myFunction(email);
};
setTimeout(nextBitOfWork, 0);
where myFunction (which recurses) now returns a promise when it's done doing it's $.ajax call.
If I simply call:
return myFunction(email);
without the setTimeout function construct above, the promise is passed through and all my promises are captured and allow me to get the array output I need and everything works great. But without the setTimeout I don't get the browser refresh. Using it as above I get the div update refresh displaying, but seem to lose the promise and so the script continues and I don't get to fill the array I use to capture values as I recurse.
Any thoughts on how to make sure the setTimeout passes on the promise?
Thanks

Performance of Javascript Function that makes HTTP Request

I need a way of measuring the response time of a javascript function that creates an http request, that includes the response time of that http request. I do not have access to modify that funciton however.
Here's what I'm trying to do:
var startTime = performance.now(); //log start timestamp
foo.bar(); //The Function that generates an HTTP request
var endTime = performance.now(); //log end timestamp
var diffTime = endTime - startTime;
but that only captures the time it takes for the function to generate the request. Is there a way to capture the response time of the request that gets genearated in javascript?
You have to check the timestamp inside of the request callback.
EDIT:
If you want do measure this completely independently from the application (so just by adding another js file), I guess you can decorate XMLHttpRequest.
window.XMLHttpRequest = function () {...}
Your wrapper will need to have the same API, so that the app still works, and can also measure performance internally.
I would usually just hit F12 to bring up dev tools on any browser, go to the network tab, and look at the ajax call that is being made, that will tell you exactly how long the request and repose took with very fine grained details.
However, if you are looking to factor in the java-script that makes the ajax call then start your timer right before the call and stop it on the complete event of the ajax call (will require editing the function). that is the only way to catch it if it is asynchronous. And just guessing here based on your description that it is actually an async call, and that explains the behavior of the function just making the ajax call then returning - and not caring about the status or the response...
If you happen to be using jQuery in your inaccessible function, you can give this a shot:
$.when(foo.bar()).done(function()
{
console.log("Fired after ajax request is done!");
});
Take a look also at:
var obj = {
promise: function() {
// do something
}
};
$.when( obj ).done( fn );
or
var obj = {
promise: foo.bar()
};
$.when( obj ).done( callback );
or check out this great article http://www.erichynds.com/blog/using-deferreds-in-jquery
well, if you don't have an acces to the function or you can't edit the ajax call, you can create a listener. the strategy:
declare a flag befor the http request call
start the call
start a listener that loop every X mili-seconds and check for a finish status (outside the ajax function and after it is called)
you should ask yourself: which kind of variables are changed when this call is over? event a visual change on the ui can be used as a flag
and if you wonder how to create your listener.. it looks something like this:
function listener()
{
// do some checks here
if (done)
{
var endTime = performance.now(); //log end timestamp
var diffTime = endTime - startTime;
}
else
{
setTimeout(listener(), 100);
}
}
listener(); // activate the listener

Sending jQuery ajax request on keyboard input

I'm sending an ajax request to the server on user's input to an <input> element, like this:
$('#my-input').bind("input", function(event){
// here's the ajax request
});
What bothers me is that it send unnecessarily many requests on every user's keyup, meaning that if the user types very fast, there are many unnecessary requests. So I get the idea that there should be a certain delay/timeout, which waits a certain time (50 miliseconds?) for the user to stop typing before sending the ajax request. That would be one problem solved.
But what about cases when the first ajax request haven't been completed before sending another request? (Typing 60 ms / char while ajax request taking 300 ms).
What is the best way to solve this problem (both idea- and code-based)?
You can use throttle function in underscore library. As its documentation says:
Creates and returns a new, throttled version of the passed function, that, when invoked repeatedly, will only actually call the original function at most once per every wait milliseconds. Useful for rate-limiting events that occur faster than you can keep up with.
Even if you don't want to introduce a new library, you can still get idea about how this function works from its source code. In fact, a simple version of throttle function could be:
function throttle(func, delay) {
var timeout = null;
return function() {
var that = this, args = arguments;
clearTimeout(timer);
timeout = setTimeout(function() {
func.apply(that, args);
}, delay);
};
}
This jQuery throttle-debounce plugin is also helpful. Especially, the debounce function seems more suitable to your needs than throttle function according to its author:
Debouncing can be especially useful for rate limiting execution of handlers on events that will trigger AJAX requests
You could just use the setTimeout function. Every so often, see if the text hasn't changed, and if it hasn't, then process accordingly.
setTimeout(function() {
// Do something after 1 second
}, 1000);
You can set async: false in your ajax request so it will process second ajax call only after completion of first ajax request.
I'd go with #HuiZeng's answer, but just in case you want a slightly modified version.
Steps
Listen to keydown using a setTimeout that you can clear.
When it fires, check if you have a previous request in queue, if so abort it and fire a new one
Example:
var inputTimer = 0, req;
function onInput(e){
clearTimeout(inputTImer);
inputTimer = setTimeout( function(){
// You have access to e here
// Cancel any previous requests
req && req.abort();
req = $.ajax({/*...Do your magic here :)*/})
}, 100)
}

JavaScript: Continue with function after another function call finishes

Edits: http://jsfiddle.net/vol7ron/wQZdM/
The fiddle should be used to help illustrate what I'm trying to do and what's happening. The sub-selects should be populated with the second option value.
Original Question:
Not sure the best way to ask. I'm creating a testing script to autofill inputs on a form.
It includes a series of drop-down select boxes, which populate other select options in an onChange event. When trying to auto-populate the form, the sub-selects don't have any options.
console.clear();
// non-select inputs
$(':input:not([type="hidden"],[type="button"])').each(function(){
$(this).val($(this).attr('name')) // test value is simple input's name
});
// select inputs
var count=0, cutoff=7500;
$('select').each(function(){
var t = $(this);
var c = t.children('option');
while( c.length <= 1 && count < cutoff){
count++;
c = $(this).children('option'); // tried not using the cache'd variable
if (!(count % 10))
console.log(count, c.length, "No Options"); // debugging -- never exists early
setTimeout(function(){},0); // not really doing anything
}
t.val( c.eq(1).val() ); // set value to second option value
t.trigger('change'); // calls the onChange even if it doesnt exist
});
// verify it does have data
console.log($('#sub-select').children('option').length); // does have options
There's an AJAX call in the change event. I could modify the callback, but this is just a simple set script for testing, that is run from console. Any ideas?
Not really sure what your code is trying to do
But answering the question How to continue with function after another function call finishes :-
assuming you have a list of functions which are all asynchronous you can nest them to continue
to the next asynchronous function ...
asyncCall(callback1) {
callback1(callback2) {
callback2(...)
}
}
Checkout https://github.com/caolan/async for some elegant ways to do this
this example calls all functions in order even though they are asynchronous.
async.series([
function(callback){
setTimeout(function(){
call_order.push(1);
callback(null, 1);
}, 25);
},
function(callback){
setTimeout(function(){
call_order.push(2);
callback(null, 2);
}, 50);
},
function(callback){
setTimeout(function(){
call_order.push(3);
callback(null, 3,3);
}, 15);
}
1) Use Synchronous AJAX request http://api.jquery.com/jQuery.ajax/
var html = $.ajax({
url: "some.php",
async: false
}).responseText;
2) Instead of using the .each use .eq(index) and just call it in order.
function FakeEach(idx) {
if(idx >= 7500) return;
var $obj = $('select').eq(idx);
if($obj.length == 0) return;
...
$obj.trigger('change');
window.setTimeout(function() { FakeEach(idx++); }, 0);
}
Your problem is that you are starting an AJAX request in the function, and expect that the response arrives before the function ends. As long as you are using an asynchronous request, this will never happen. You have to exit your function before the code that handles the response can run. Javascript is single threaded, so as long as your function is running, no other code can run.
The solution to the problem is to put the code that uses the data in the success callback function that is called after the response has arrived. Eventhough you usually write that function inside the function that makes the AJAX call, it's a separate function that will run later.
If you really need the data in the same function as the AJAX call, you would need to make a synchronous request. This is a killer for the user experience, though, as the entire browser freezes while it is waiting for the response.

Categories

Resources