AJAX requests not executing in parallel using $.when(...) - javascript

My script makes around 15 ajax calls to my server. I want them to execute in parallel. I am trying to use when to execute a bunch of ajax requests in parallel but for some reason it's still doing them one by one.
My code is below:
var requests = $.map(results, function(result, i) {
var suggestionContainer = $('<div class="suggestion-' + i + '">' + result + '</div>');
resultsContainer.append(suggestionContainer);
return $.get('check.php', { keyword: result }).done(function(res) {
res = JSON.parse(res);
suggestionContainer.append(generateSocialMediaList(res.social_media))
.append(generateDomainList(res.domains));
}).fail(function() {
suggestionContainer.remove();
});
});
$.when(requests).done(function() {
console.log('Complete')
}, function() {
alert('Something Failed');
});
Is there anything I'm doing wrong?
The reason why I am making 15 requests is because check.php makes a call to a third party API. The API is slow, and unfortunately there is no alternative. Making 15 parallel requests would be much quicker than 1 request and wait for check.php to complete.
The code works like so:
A request to suggestions.php is made (that's not included as it's not required to solve the problem). The results are stored in an array results.
The results (there's about 15) are returned and iterated over with map.
I insert the suggestion into the page. I then return an promise.
The requests array now contains 10-15 promises.
I use when to execute the requests in parallel (that's what I'm trying to do at least).
Upon success the DOM is updated and the results from check.php are inserted into the DOM.

Your code will execute the requests with as much parallelism as the browser allows.
Browsers used to limit concurrent requests to a single domain to 6. This may have changed.
The responses to these requests will be serviced one by one because JavaScript is single-threaded, and this is what you are observing.
Also: double-check your AJAX call is not failing. when will reject as soon as any of the promises is rejected.

Related

Ajax calls DURING another Ajax call to receive server's task calculation status and display it to the client as a progression bar

I'm trying to figure out if there's any chance to receive the status of completion of a task (triggered via an ajax call), via multiple (time intervalled) ajax calls.
Basically, during the execution of something that could take long, I want to populate some variable and return it's value when asked.
Server code looks like this:
function setTask($total,$current){
$this->task['total'] = $total;
$this->task['current'] = $current;
}
function setEmptyTask(){
$this->task = [];
}
function getTaskPercentage(){
return ($this->task['current'] * 100) / $this->task['total'];
}
function actionGetTask(){
if (Yii::$app->request->isAjax) {
\Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
return [
'percentage' => $this->getTaskPercentage(),
];
}
}
Let's say I'm in a for loop, and I know how many times I iterate over:
function actionExportAll(){
$size = sizeof($array);
$c = 0;
foreach($array as $a){
// do something that takes relatively long
$this->setTask($size,$c++);
}
}
While in the client side i have this:
function exportAll(){
var intervalId = setInterval(function(){
$.ajax({
url: '/get-task',
type: 'post',
success: function(data){
console.log(data);
}
});
},3000);
$.ajax({
url: '/export-all',
type: 'post',
success: function(data){
clearInterval(intervalId); // cancel setInterval
// ..
}
});
}
This looks like it could work, besides the fact that ajax calls done in the setInterval function are completed after "export-all" is done and goes in the success callback.
There's surely something that I'm missing in this logic.
Thanks
The problem is probably in sessions.
Let's take a look what is going on.
The request to /export-all is send by browser.
App on server calls session_start() that opens the session file and locks access to it.
The app begins the expensive operations.
In browser the set interval passes and browser send request to /get-task.
App on server tries to handle the /get-task request and calls session_start(). It is blocked and has to wait for /export-all request to finish.
The expensive operations of /export-all are finished and the response is send to browser.
The session file is unlocked and /get-task request can finally continue past session_start(). Meanwhile browser have recieved /export-all response and executes the success callback for it.
The /get-task request is finished and response is send to browser.
The browser recieves /get-task response and executes its success callback.
The best way to deal with it is avoid running the expensive tasks directly from requests executed by user's browser.
Your export-all action should only plan the task for execution. Then the task itself can be executed by some cron action or some worker in background. And the /get-task can check its progress and trigger the final actions when the task is finished.
You should take look at yiisoft/yii2-queue extension. This extension allows you to create jobs, enqueue them and run the jobs from queue by cron task or by running a daemon that will listen for tasks and execute them as they come.
Without trying to dive into your code, which I don't have time to do, I'll say that the essential process looks like this:
Your first AJAX call is "to schedule the unit of work ... somehow." The result of this call is to indicate success and to hand back some kind of nonce, or token, which uniquely identifies the request. This does not necessarily indicate that processing has begun, only that the request to start it has been accepted.
Your next calls request "progress," and provide the nonce given in step #1 as the means to refer to it. The immediate response is the status at this time.
Presumably, you also have some kind of call to retrieve (and remove) the completed request. The same nonce is once again used to refer to it. The immediate response is that the results are returned to you and the nonce is cancelled.
Obviously, you must have some client-side way to remember the nonce(s). "Sessions" are the most-common way to do that. "Local storage," in a suitably-recent web browser, can also be used.
Also note ... as an important clarification ... that the title to your post does not match what's happening: one AJAX call isn't happening "during" another AJAX call. All of the AJAX calls return immediately. But, all of them refer (by means of nonces) to a long-running unit of work that is being carried out by some other appropriate means.
(By the way, there are many existing "workflow managers" and "batch processing systems" out there, open-source on Github, Sourceforge, and other such places. Be sure that you're not re-inventing what someone else has already perfected! "Actum Ne Agas: Do Not Do A Thing Already Done." Take a few minutes to look around and see if there's something already out there that you can just steal.)
So basically I found the solution for this very problem by myself.
What you need to do is to replace the above server side's code into this:
function setTask($total,$current){
$_SESSION['task']['total'] = $total;
$_SESSION['task']['current'] = $current;
session_write_close();
}
function setEmptyTask(){
$_SESSION['task'] = [];
session_write_close();
}
function getTaskPercentage(){
return ($_SESSION['task']['current'] * 100) / $_SESSION['task']['total'];
}
function actionGetTask(){
if (Yii::$app->request->isAjax) {
\Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
return [
'percentage' => $this->getTaskPercentage(),
];
}
}
This works, but I'm not completely sure if is a good practice.
From what I can tell, it seems like it frees access to the $_SESSION variable and makes it readable by another session (ence my actionGetTask()) during the execution of the actionExportAll() session.
Maybe somebody could integrate this answer and tell more about it.
Thanks for the answers, I will certainly dig more in those approaches and maybe try to make this same task in a better, more elegant and logic way.

Chained jQuery AJAX with promise

I am currently working on a project where 4 get requests are fired simultaneously. I am at the same time using fade effects, and the asynchronous nature of this results in empty data intermittently.
I have been looking into this method as described in
Prefer way of doing multiple dependent ajax synchronous call to replace how I am currently doing
$.get('ajax_call_1').then(function(value) {
return $.get('ajax_call_2');
}).then(function(result) {
// success with both here
}, function(err) {
// error with one of them here
});
But, my question is: How can I access the return from each request individually with the above?
You've said the requests are sent simultaneously. The way you've written your code, they are sent sequentially though. Instead, with Promise.all, you can wait for all of the requests' promises and you'll be given an array with the results:
Promise.all([
$.get('ajax_call_1'),
$.get('ajax_call_2'),
$.get('ajax_call_3'),
$.get('ajax_call_4')
]).then(function(results) {
var first = results[0];
var second = results[1];
...
}).catch(function(err) {
// called if one of the requests fails
});

First ajax call goes extremely slow, subsequent calls run quickly -- why?

I'm using a simple jQuery AJAX function that runs extremely slow (10-15 seconds) the first time it's called, and then runs normally at <1 - 2 seconds each time it's called after that first time. I cannot figure out why this is happening but need to speed it up as much as possible. Here is the function:
function getNewItemAlt(apiUrl, callType, apiKey, dataType, returnValue, appendToWrapper) {
// ajax call to the api
return $.ajax({
type: callType,
url: apiUrl,
data: apiKey,
dataType: dataType,
success: function(result) {
appendToWrapper.closest('.game_play_area').find('.game_loader').remove();
// this is the thing that we want (probably either
// an image url or an actual value)
var desiredReturn = deepValue(result, returnValue);
var specialClass = '';
console.log(typeof desiredReturn)
if (typeof desiredReturn === 'number') {
specialClass = 'number'
}
// if it's a URL then it's an image and can be setup
// in an imgage tag and added to the dom
if (desiredReturn.toString().substring(0, 4) == "http") {
$(appendToWrapper).children('.game_image').remove();
$(appendToWrapper).prepend('<img class="game_image" src="' + desiredReturn + '" />');
} else {
$(appendToWrapper).children('.game_value_return').remove();
$(appendToWrapper).prepend('<p class="game_value_return ' + specialClass + '">' + desiredReturn + '</p>');
}
// clear the space to play the game
// $(currentGameWrapper).children('.game_intro').remove();
// show the game
// currentGameWrapper.children('.game_play_area').removeClass('hide');
},
error: function(err) {
console.log(err);
}
});
}
An example of an API that I'm making a request to is the Giphy API. I'm not convinced this is a server issue because it happens only on the first call to the api and then the subsequent calls are speedy.
Any ideas why this is happening and what can be done to make this run faster?
Considering the whole issue Javascript (client side) + API (server side) could complicate diagnosing the issue, so my suggestion to get a more specific answer would be to isolate the issue first.
Answering your general question, Reasons why?: It could be a lot of things but the remarkable ones are:
Handshake: the first interaction between your page and the server makes the remote server to authenticate you and validate your session. Later calls wont go through that process.
Server first execution: (less probable if you are using public APIs) if you are using a remote server with Java for example, that you are restarting, the first time you call a service it will load the instances, but for future calls those instances are already created hence they respond faster.
Network: (I don't think so... but...) trace your HTTP request to see how many jumps it has and how much is taking for them to be resolved.
How to Diagnose (isolation): Measure the time each step takes, it could be a simple print of your current time. I would break it the in the following steps:
Preparing the call to the API.
Calling the API.
Getting the data.
Manipulate the received data on the client side.
NOTE: steps 2 and 3 could go together.
How to mitigate this from happening (it doesn't solve the issue, but mitigates it):
Handshake: if the issue is related with authentication/authorization I recommend you to do an empty pre-fetch (without requesting any data) to deal with the handshake. Then you do a data fetch when you need it without that overhead.
Server first execution: you don't have too much to do here unless you own the server also. In this case I recommend also a pre-fetch but calling the entire service to initialize the server objects.
Javascript API: if the problem is dealing with the data on your client side then review how to optimize your Javascript code.
This might be a long shot. "appendToWrapper" is an object passed in by reference. If it's taking a long time to resolve (ie it takes 10 seconds or so to find ".game_play_area" etc... in the DOM) then it would be slow the first time, but saved in the calling function and fast subsequent times.
It might be easy to check. If you could make a deep copy of the object before passing it in, we would expect the AJAX to be slow every time, not just the first time. If that's the case, you'd need to clean up your selectors to make them faster. Perhaps use ids instead of classes.

ajax requests are Pending parallel request

What im doing is when page is loading im calling two ajax request
fetch_list_big();
fetch_list_small();
function fetch_list_big(){
$.post(...);
}
function fetch_list_small(){
$.post(...);
}
As the name suggests request in fetch_list_big() takes more time to complete than fetch_list_small.
But since fetch_list_big is called first the fetch_list_small says pending till fetch_list_big returns 200.
big.php
require_once('files_same.php'); #starts session /connection / configurations etc
#Some heavy mysql stuff #say 5 seconds
echo json(...)
small.php
require_once('files_same.php'); #starts session /connection / configurations etc
#Some light mysql stuff #say 1 seconds
echo json(...)
How can i call fetch_list_small() after fetch_list_big() in parallel way and not make it pending ?
Pending Requests
http://i.imgur.com/vj07tyI.png
The first request is huge and takes 5 second in server
The last 3 are small request and should be returned before first one but they are pending.
After First request returns 200
http://i.imgur.com/liPuO70.png
After first request returns 200 . the last 3 requests are executed.
Problem
I want all the requests to Run Parallel without locking in server (is some kind of session is getting locked ? )
I want all the requests to Run Parallel without locking in server (is some kind of session is getting locked ? )
Yes; PHP will block other scripts from accessing the session, as long as one script instance is using it. (At least for the default file-based session storage mechanism.)
You can avoid this by calling session_write_close as soon as your script(s) are done with what they need to do with the session.
You can use a callback in your fetch_list_big()
function fetch_list_big(callback){
$.post(url, function(data){
if(callback){
callback();
}
});
}
fetch_list_big(function(){
fetch_list_small();
});

jQuery: AJAX request fires multiple times until response is successfully using $.Deferreds

Problem!
While trying to perform a set of AJAX request, most of the time at least one of the request is always getting a pending response, this is resulting in a loop of requests until it gets a succesful response. Please note that I using jQuery.when, this way I can ensure that both requests have been executed.
The mentioned behaviour is resulting on:
Multiple requests to the same source
jQuery.always is executes as many times as requests performed
The interface is crashing due to multiple updates on it's DOM.
Example
var request = [];
request.push(getProductPrice().done(
function(price) {
updateProductPrice(price);
}
);
request.push(getProductInfo().done(
function(information) {
updateProductInformation(information);
}
);
jQuery.when.apply(undefined, request).always(function() {
doSomeStuff1();
doSomeStuff2();
...
...
...
doSomeStuffN();
});
function updateProductPrice(obj) {
return jQuery.get(...);
}
function updateProductInformation(obj) {
return jQuery.get(...);
}
Questions?
Is there any reason on why I am getting a pending response?
Is this problem realted to jQuery.when trying to release the AJAX request in order to fire-up the callbacks?
Facts
If I do the request to the mentioned sources via synchronous, I will never get a pending response. I am just trying to avoid the use of async: false.
Update #1
By pending status I meant the response given by the web browser to my request, which is nothing but the ajax call waiting for it's response. The main problem resides on how those AJAX request are being treated, I am noticing that the functions updateProdcutPrice() and updateProductInformation() are being called N times until the response from the server is succesful, this is resulting that the functions declared on the .always()'s callback for the requestes performed on updateProdcutPrice() and updateProductInformation() are also being called that many times.

Categories

Resources