Call multiple ajax simultaneously in Magento2 [Concurrent Request Blocking in magento2] - ajax

I am trying to import data with one ajax and trying to do live tracking of this imported data with another ajax to show progress bar(how many records have been uploaded).
checkPrgressVar = setInterval(checkPrgress, 1000);
importTaxes(importURL,cart_url,cart_token);
/* check progress of imported data */
function checkPrgress()
{
$.ajax({
url: $(document).find("input[name='progressURL']").val(),
type: 'POST',
dataType: 'json',
// showLoader: true,
data: "action=import_store",
complete: function(response) { debugger
var response = response.responseJSON.output;
},
error: function (xhr, status, errorThrown) {
console.log('Error happens. Try again.');
}
});
}
/* import data */
function importTaxes(importURL,cart_url,cart_token)
{
$.ajax({
url: importURL,
type: 'POST',
dataType: 'json',
showLoader: true,
data: "cart_url="+cart_url+"&cart_token="+cart_token+"&action=import_tax",
complete: function(response) { debugger
var response = response.responseJSON.output;
if(response.result == 'processing')
{
}
else
{
}
},
error: function (xhr, status, errorThrown) {
console.log('Error happens. Try again.');
}
});
}
My checkProgress ajax returns response only after getting response from importTax ajax while this checkProgress ajax should be independent of importProgress ajax.
However both ajax is calling different controllers, it seems magento is not allowing another controller to be called until it processed another one.
I don't know why it happens and how to resolve it ?
I have tried this link but not working.
Is it possible to fire multiple ajax requests asynchronously in Magento 2 backend?
EDIT1: what I have figured out that if I call another external URL instead of magento2 controller in checkProgress ajax call. it started working. It means magento2 is not allowing another controller to be execute while another is running via import ajax.
EDIT2- Both ajax started to work as expected if I change session storage from "files" to "db" in env.php file. It seems a session blocking issue as explained in below link-
Two simultaneous AJAX requests won't run in parallel
But use DB as session storage is not preferable. So If I use files as session storage still I don't know how to resolve it.
Have any one idea about it ?

Finally I have figured out its solutions.
The main reason of not running both AJAX concurrently was session blocking issue as explained in below URL.
Two simultaneous AJAX requests won't run in parallel
but session_write_close() didn't work for me. So after research I have found my solution and here it is -
I used Magento\Framework\Session\SaveHandler to close and open session in magento2.
I load this class in construct in one of my controller file(which handles ajax) -
/**
* #var \Magento\Framework\Session\SaveHandler
*/
protected $sessionHandler;
public function __construct(
\Magento\Backend\App\Action\Context $context,
\Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory,
\Magento\Framework\View\Result\PageFactory $resultPageFactory,
\Magento\Framework\App\ResourceConnection $resourceConnection,
\Magento\Framework\Session\SaveHandler $sessionHandler
) {
parent::__construct($context);
$this->resultJsonFactory = $resultJsonFactory;
$this->resultPageFactory = $resultPageFactory;
$this->connection = $resourceConnection;
$this->sessionHandler = $sessionHandler;
}
Then in execute function, I closed and open session like this-
public function execute()
{
$tmp_session_dir = ini_get("session.save_path");
$this->sessionHandler->close();
$this->sessionHandler->open($tmp_session_dir,"admin");
// my code here
}
ini_get("session.save_path") gives me the path of directory on server where session files are saved.
Now my both ajax run concurrently and independent of each other.

Related

Preventing Ajax save data two times

Whenever I run Ajax in jquery, it will run two times. It will cause a problem in saving data. I googled and used their metods but none of them worked. The following is the ways I tried:
Set a global flag. Set it on when Ajax is running, turn it off after done. It seems that duplicated calls manage the flag concurrently. Before call 1 closed the door, call 2 is already in in no time. Or check time if less than 4000, do not run Ajax.
Generating a random number and attach to the url, they are the same always.
event.preventPropgador();e.preventDefault(); does not work in whichever order
async:true or false does not work
I cannot use "once" since I am usimg jQuery Mobile
$("#submitButton").once("tap", function () { //<-- error
save();
});
used "one" but not work
$("#submitButton").one("tap", function () {
save();
});
disable the button when ajax running, then enable it afterwords.
I tried above techs on save() and Ajax calls, none of them worked.
This is my code like:
$(document).bind("pageinit", function () {
$("#submitButton").one("tap", function () {
save();
});
}
function save() {
$.ajax({method: "get",
url: baseUrl + "save?model=abcd&rand=" + randonNum(),
contentType: "application/json; charset=utf-8",
success: function (response) {
//do whatever
}
},
error: function (request, status, error, errorThrown) {
}
});
}

Why is this basic ajax call giving me a 400 and not firing to the controller?

This is only a problem for me in my newest dot net core 2 web app, it works in previous dot net apps with no issue.
I have a custom javascript file with paths to various servers that my site is hosted on(mainly dev, test, live).
My ajax call looks like the following:
var gameid = '#Html.Raw(Model.Id)';
$.ajax({
type: 'GET',
url: url() + "UserGames/HasGame?id=" +gameid,
cache: false,
success: function (data) {
console.log(data);
},
error: function (req, status, err) {
console.log('Something went wrong', status, err);
}
});
The url() in question is simply looking at my local server while testing(*please note, this port has been edited, it is correct):
function url() {
return "https://localhost:4432/";
}
The error is not firing as it isnt even reaching the controller function being used.
I'm absolutely bemused as to why this isnt working, i could understand if i was getting some sort of error back, but i have used ajax calls in all of my apps and not had this problem at all until using core 2.
Has anything changed that may have affected the way you use ajax calls?
Method that is trying to fire, but i mentioned, it isnt hitting the controller(NOTE: the method does have code in it, it just isnt hitting the controller in the first place)
[HttpGet]
[ValidateAntiForgeryToken]
public async Task<IActionResult> HasGame(int id)
{
//stuff in here
}
You are getting a 400 (Bad Request) response because the framework expects the RequestVerificationToken as part of the request.The framework uses this to prevent possible CSRF attacks. If your request does not have this information, the framework will return the 400 bad request. Your current code is not sending it.
You can fix it by explicitly send that RequestVerificationToken
The form tag helper automatically creates a hidden input with name atttribute value __RequestVerificationToken and stores the token as the value of the hidden input.
var token = $("[name='__RequestVerificationToken']").val();
var gameid = '#Html.Raw(Model.Id)';
$.ajax({
type: 'GET',
url: url() + "UserGames/HasGame?id=" +gameid,
headers:{ "RequestVerificationToken": token },
success: function (data) {
console.log(data);
},
error: function (req, status, err) {
console.log('Something went wrong', status, err);
}
});
In the above example, we are using some jQuery code to get the input element with name __RequestVerificationToken and reading the value of it. A more robust approach would be injecting the IAntiforgery implementation to the view and using the GetAndStoreTokens method.
We will be injecting IHttpContextAccessor to the view. So make sure it is wired up with DI properly. Add this line to your Startup classes' ConfigureServices method.
services.AddTransient<IHttpContextAccessor, HttpContextAccessor>();
Now in your view, you can inject IHttpContextAccessor and IAntiforgery and then call the GetAndStoreTokens to get the token. Here i wrapped that into a helper function.
#inject Microsoft.AspNetCore.Http.IHttpContextAccessor HttpContext
#inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Xsrf
#functions{
public string GetAntiXsrfRequestToken()
{
return Xsrf.GetAndStoreTokens(HttpContext.HttpContext).RequestToken;
}
}
Now later in my js, i can call this method inside my javascript code instead of using jQuery to get the input value.
var token = "#GetAntiXsrfRequestToken()";
var gameid = '#Html.Raw(Model.Id)';
$.ajax({
type: 'GET',
url: url() + "UserGames/HasGame?id=" +gameid,
headers:{ "RequestVerificationToken": token },
success: function (data) {
console.log(data);
},
error: function (req, status, err) {
console.log('Something went wrong', status, err);
}
});
Or Simply remove [ValidateAntiForgeryToken] attribute decoration from the action method, which excludes this check.
I recommend you to take advantage of the[ValidateAntiForgeryToken] method. Send the token as part of your request(first approach)
If anyone else comes across this issue, i managed to fix it by doing the following to both the call and the controller:
type: 'GET',
url: '#Url.Action("HasGame", "UserGames")',
data: {id : gameid},
And the controller in core 2 does not seem to like you declaring [HttpGet] at all, so i removed that also.
Thanks for all the help provided.

get data from ajax as an attribute value for callback function

Im new to ajax. I was trying to find the answer but was not lucky to find the corresponsing one. Basically I need to use an ajax to get some data and after that to put this data to the variable that later will be used as an attribute for the callback function with custom code.
This ajax part is just a method of myObject.
So, in the end I need this kind of functionality:
myObject.getData(url, callback(data) {
//my custom code of what I wanna do after ajax is complete
});
My code
/*
HERE COME SOME PROPERTIES AND OTHER METHODS WICH IS NOT THE CASE
*/
//This is where Im stuck
var getData = function getFromUrl($url) {
$.ajax({
type: 'get',
url: $url,
dataType: 'html',
success: function(html) {
$obj = html;//Im lost on this step!
},
});
};
P.S. Im trying to find an async way (without using async:false). Hope its possible
First I encountered many problems. My first problem was No Access-Control-Allow-Origin, most websites dont allow you to just scrap get their data for security reasons. Luckily someone already made a proxy: http://cors.io/ . Second problem is that you cant embed http on https, so I cant use jsfiddle to show you this working, it works on my local enviroment. After you get the raw html you have to parse it, you can do it with full regex, or you can power yourself with jquery like I'm doing on this example. What we're doing is checking stackoverflow.com and getting the amount of featured questions with .find(".bounty-indicator-tab").first().html(); But once you have the full html you can get any data you need.
var getData = function getFromUrl(url) {
$.ajax({
url: 'http://cors.io/?' + url,
crossDomain: true,
dataType: 'html',
success: function (html) {
var match = $(html).find(".bounty-indicator-tab").first().html();
console.log(match);
return match;
},
error: function(e) {
console.log('Error: '+e);
}
});
};
url = 'http://stackoverflow.com/';
data = getData(url);
//You cant use data yet because its working async

Symfony2: ajax call redirection if session timedout

I have a working dashboard with ajax request. I fire an ajax request on some events which will update a part of the dashboard. But if the session has expired, the part will be refreshed with the login page. How can i do a redirection after the ajax call if the session has expired ?
My ajax call :
$.ajax({
type: "POST",
url: $(this).data('path'),
data: { datas : {
/* some datas */
}},
success: function(data){
$('#mydivtorefresh').html(data);
},
error: function(){
showFlash();
},
});
and my controller :
public function myControllerAction(Request $request)
{
/* some logic */
return $this->render('my/template/toUp.html.twig',array('results' => $results));
All is working well, but if my session expires and i call this ajax request, i will get the login page in the '#mydivtorefresh' instead of a global redirection. I tried with eventListener or with AjaxError callback but with no success. Any help ?
Found a working solution :
Add a function in my global controller which check if session is still active and responds with "ok" if yes, "ko" if not.
public function pingAction()
{
$securityContext = $this->container->get('security.context');
if ($securityContext->isGranted('IS_AUTHENTICATED_REMEMBERED') || $securityContext->isGranted('IS_AUTHENTICATED_FULLY')) {
return new Response("ok");
}
return new Response("ko");
}
and i check it every times an ajax call is fired using the preFilter jquery event :
$.ajaxPrefilter(function(options, originalOptions, jqXHR) {
if(originalOptions.data == "CHECKPING"){
return;
}
$.get( Routing.generate('el_ping'), "CHECKPING", function( res ) {
if(res == "ko"){
window.location.replace(Routing.generate('fos_user_security_login'));
}
});
});

Ajax request error when changepage

guys. I have a juerymobile multi-page, and I have a button in #page-index, when click it, will send a ajax request to server, and changepage to #page-column, It run will in PC, but when i deploy the multi-page in phonegap, the button click can just run only twice, code is below:
function test()
{
$.mobile.changePage('#page_column');
$.ajax({
url: "http://192.168.168.120:8090/fcmobile/getTest",
dataType: "json"
}).done(function(data) {
alert(data.content);
});
}
I found if I remove $.mobile.changePage('#page_column');, the ajax request can be run well any times. but when I add the changePage code, it only can be run twice, in third time, ajax request can't even be send. Dose anybody know reason?
AJAX is made to be asynchronous, so no need to set async to false to get it working. Use events instead.
For example:
function test () {
$.ajax({
'url': "http://192.168.168.120:8090/fcmobile/getTest",
'dataType': 'json',
'success': function (json_data) {
$(document).trigger('test_json_data_loaded');
console.log(data);
}
});
}
$(document).on('test_json_data_loaded', function () {
$.mobile.changePage('#page_column');
});
When you set async to false, you're basically making it so that every time this AJAX request is made, the user will have to wait until all the data is fully loaded before the application/website can do/execute anything else...not good.

Resources