I have tried ways to search for a solution but I can't seem to find the right combination of words or something... here goes:
I have an ASP.NET MVC application that users scan inventory/package barcodes into. Every time someone scans an item, I make an async request and then display a popup message with information about the package. This part works as expected and does not block the application during the request:
$.ajax({
type: 'GET',
dataType: 'json',
async: false,
url: '#Url.Action("SingleOrderLookup")?trackingNumber=' + trackingId,
success: function (result) {
if (result.success) {
var audio = findAudio(result.model, audioClips, saturdayAudio);
suppressDefaultSound = true;
var titleText = result.model.displayPromptText;
if (result.model.isRefrigerated) {
isRefrigerated = true;
titleText = "<p style='color: blue;'>(REFRIGERATED)</p>" + "<p>" + result.model.displayPromptText + "</p>";
}
swal.fire({
title: titleText,
text: "Place in route for " + result.model.displayPromptText,
type: "success",
showCancelButton: false,
confirmButtonText: "Sorted",
cancelButtonText: "Cancel",
timer: 1750,
preConfirm: function () {
return new Promise(function (resolve) {
resolve();
}, 1000);
}
}).then(result => {
if (result.value) {
}
});
var dupe = findOrderByTrackingNumber(trkNumbers, result.model.trackingId);
if (!dupe) {
trkNumbers.push({ trackingNumber: trackingId, depotId: result.model.destinationHub });
pkgCount++;
if ($("#divUpdatePickup").is(":hidden"))
$("#divUpdatePickup").show();
AddLogToTable(trackingId);
} else {
//audible feedback that duplicate was scanned
//if (!trkBin) PlayAudio(2);
//PlayAudio(2);
}
//playing audio
if (isRefrigerated) {
setTimeout(function () {
if (audio) playByteArray(audio);
}, 1500);
PlayRefrigerate();
} else {
if (audio) playByteArray(audio);
}
}
if (result.nullRoute) {
addToTrkNumbers = false;
Swal.fire({
title: "NO ROUTE DEFINED",
text: "Unable to match order to a route!",
type: "warning",
showCancelButton: false
});
}
}
});
However, I want the page to make another async call to populate a variable with an array of objects, transparently and without blocking the user from making scans and receiving information back from the async calls from the above code. This call should occur immediately when the page is loaded, and it could take more than a minute or two to receive all the data expected from this call. Once the response is back, the collection variable (zipSort[]) should be populated. The data in this variable will contain a "cache" of elements that the page can query against to avoid having to make individual server-side calls after each scan (in essence, I want to "front-load" data needed for the scan events and once completed, individual calls to the server should not be necessary since this variable should contain 99% of the IDs expected to be scanned).
This is where I'm having an issue and it's probably due to a lack of understanding of how async calls/JS promises work. Here is the code I have so far for this:
//array to hold data on expected tracking number scans
var zipSort = []
async function getCheckinGroup(zipSort) {
console.log("Fetching complete check-in group...");
var url = '/SortFacility/HubManager/GetOrders';
var promise = new Promise((resolve,reject) => {
$.ajax({
type: "GET",
url: url,
cache: false,
async: true,
contentType: "application/json",
success: function (result) {
if (result.success) {
console.log("Retrieval success");
try {
zipSort = result.model;
resolve(result.model);
} catch (ex) {
reject("Some error?");
}
} else {
reject("Some error?");
}
},
error: function (ob, errStr) {
reject("Something went wrong");
}
});
});
return promise;
}
//don't want this to hold up execution of the rest of the code, so zipSort[] should
//remain empty and get set transparently when the ajax response is returned:
getCheckinGroup(zipSort);
Every version of code I'm trying out from articles and tutorials I have read holds up the UI and keeps users from being able to scan items while the response hasn't been returned. What am I missing? How should I change this so that (a) users can begin scanning immediately once the page has loaded and receive information from individual async calls to the DB, and (b) zipSort[] can be populated with the totality of any data potentially needed for these scans, and once populated, scan events trigger a lookup on that variable instead of continued individual calls to the database?
Any help would be appreciated!
Edit: tried simply adding this call in-line and no matter where I put it, it blocks the other code from running until response is received, even though async is set to true:
$.ajax({
type: "GET",
url: url,
cache: false,
async: true,
contentType: "application/json",
success: function (result) {
console.log("Data received.");
zipSort = result.model;
}
});
Thanks everyone for your help. I found this little gem, which solved my problem:
https://josef.codes/c-sharp-mvc-concurrent-ajax-calls-blocks-server/
Applying [SessionState(System.Web.SessionState.SessionStateBehavior.Disabled)] to my controller class enabled concurrent async ajax calls.
Related
i am trying to use sweetAlert2 for my ajax, creating a form inside and then do its process and get the results back, but i am stuck at one point which is when i send the results how do i process the ajax inside it,
Here is my code as of now
Swal.fire({
title: 'Request PlayForm',
html: `<textarea name="da" id="da"></textarea>`,
confirmButtonText: 'Submit',
focusConfirm: false,
preConfirm: () => {
const textData = Swal.getPopup().querySelector('#da').value
if (da== '') {
Swal.showValidationMessage(`Please enter details.`)
}
return { da: da}
}
}).then((result) => {
Swal.fire(`
Email Sent Successfully -- this message should come when i get a success from my ajax else it will display error which i can get from ajax
`.trim())
})
You can do it like this.
Swal.fire({
title: 'Request PlayForm',
html: `<textarea name="da" id="da"></textarea>`,
confirmButtonText: 'Submit',
focusConfirm: false,
preConfirm: () => {
const textData = Swal.getPopup().querySelector('#da').value;
if(!textData || !textData.trim()) {
Swal.showValidationMessage(`Please enter details.`)
}
return textData;
}
}).then((result) => {
var myResult = result.value;
console.log("calling ajax");
$.ajax({
url: 'https://pokeapi.co/api/v2/pokemon/' + myResult,
//data: {'da':myResult}, <== use this if you're sending any data
type: 'get', // or post, depending what you do in the background,
// dataType: 'json' - data type of your response. It's optional,
// you can set it to something else, like text, or application/pdf,
//or something else
beforeSend: function() {
console.log("this is before send - we want to get some info " + myResult);
// disable buttons to prevent double clicks,
// or do something else
},
success: function(data) {
// Process the response - data
// Send mail if successful
if(data) {
Swal.fire(`
Email Sent Successfully -- this message should come
when i get a success from my ajax else it will display
error which i can get from ajax
`.trim());
console.log(data);
} else {
Swal.fire(`There was an error: ` /* your error here*/);
}
},
error: function(desc, err) {
Swal.fire(`
AJAX error! Description: ` + JSON.stringify(desc) + `,
error: ` + JSON.stringify(err));
}
});
// END AJAX
});
// END .then(...)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.3/jquery.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/sweetalert2#11"></script>
The variable myResult (you can name it whatever you like) stores the result of da / return of your preconfirm. You could process it later on, to see if it matches what you expect (for example, whether there were any illegal characters, or if you were expecting a number, or a certain format, but the user decided to be cheeky and type in something else, etc).
If the input was alright, you move on to the else part in the .then(...), and call your AJAX there. Read the comments in the code for more info.
So I have an array that contains data that needs to be sent as part of the payload for POST requests that need to be made. Now, these need to be made sequentially, since I need to display the particular payload , then the result(the result being the response that is returned after making the POST request with the payload), then the next payload, then the next result and so on, on my page. I was just wondering what would be the best approach to accomplish this.So this is how I'm approaching it at the moment:
for (var i = 0; i < quotesList.length; i++) {
var response = $.ajax({
type: "POST",
url: url,
data: JSON.stringify(quotesList[i]),
success: add_to_page,
},
timeout: 300000,
error: function(){
console.log("Some Error!")
},
contentType: "application/json"
})
Obviously, the issue here that arises is there's no guarantee in terms of the getting that sequence right and also I wasn't quite sure how exactly to keep a track of the payload since trying to add it as a part of the success function only gets me the last element in the last(presumably the last request thats fired)
You can try something like below.
let quotesListIndex = 0;
function sendAjaxCall() {
if (quotesListIndex < quotesList.length) {
var response = $.ajax({
type: "POST",
url: url,
data: JSON.stringify(quotesList[quotesListIndex++]),
success: function () {
//... Add code here
sendAjaxCall(); // Call the function again for next request
},
timeout: 300000,
error: function () {
console.log("Some Error!")
},
contentType: "application/json"
})
} else {
return;
}
}
Add one global variable to track the index. and call function recursively. Once the index reached the array size then return from function.
What I am trying to do:
1. Initially gives an ajax request to the server based on some inputs
2. The server returns a job id generated by RQ (Python-rq)
3. Based on the job id ajax request made to a url constructed with the jobid regularly till a valid response is obtained
What I have:
$.ajax({
type: "POST",
url: "/start",
data:{crop: valueCrop, state: valueState, variablemeasure: valueVariable, unit:unitMeasure, from:yearFrom, to:yearTo},
success: function(results) {
console.log(results);
var jobId='';
jobId = results;
function ajax_request() {
$.ajax({
type: "GET",
url: "/results/" + jobId,
dataType: "json",
success:function(xhr_data) {
if (xhr_data == {"status":"pending","data":[]}){
console.log("Waiting for response");
setTimeout(function() { ajax_request(); }, 2000);
} else {
console.log(xhr_data);
}
},
error:function(error) {
console.log(error)
}
});
}
},
error: function(error) {
console.log(error)
}
})
Is this even possible? I am not getting any output at all on the console although the rq says the job is finished. I think it is not entering that if loop. When I visit the "/results/jobId" url I am able to see the result.
Please help.
I see a few bugs in this code. First of all, you have defined the function ajax_request(). But you are not calling it. You can call it at the end of its definition.
Secondly, this code is problematic:
if (xhr_data == {"status":"pending","data":[]})
The object notation creates another object which is definitely not equal to xhr_data.
You can do:
if (xhr_data.status === "pending")
I know that the above can be achieved by using quietMillis in the AJAX call, but I am using query to cache the data. And it is here I am not able to delay the AJAX call. Below is the code
$('#AssetType').select2({
cacheDataSource: [],
placeholder: ' ',
quietMillis: 3000,
query: function q(query) {
self = this;
var key = query.term;
var cacheData = self.cacheDataSource[key];
if (cacheData) {
query.callback({
results: $.map(cacheData, function (item) {
return {
text: item.LongDescription,
id: item.AssetTypeID
}
})
});
return;
}
else {
$.ajax({
url: 'http://localhost:52377/api/reference/asset/types/' + key,
dataType: 'json',
type: 'GET',
quietMillis: 3000,
//data: function (query) {
// return { assetType: query.term, };
//},
success: function (data) {
self.cacheDataSource[key] = data;
query.callback({
results: $.map(data, function (item) {
return {
text: item.LongDescription,
id: item.AssetTypeID
}
})
});
},
cache: true
})
}
}
});
Is there any work around to delay the AJAX call so that the AJAX call is not fired for every keystroke?? The reason for using "query" is for caching, which is not achieved just by setting cache to true in the AJAX call.
According to the select2 documentation, you can do this easily.
A request is being triggered on every key stroke, can I delay this?
By default, Select2 will trigger a new AJAX request whenever the user changes their search term. You can set a time limit for debouncing requests using the ajax.delay option.
This will tell Select2 to wait 250 milliseconds before sending the request out to your API.
$('select').select2({
ajax: {
url: '/example/api',
delay: 250
}
});
I found a way to delay the triggering. I've used an implementation of debounce function in underscore.js . The code would now look like this
query: debounce(function q(query) {..
.....
}, 350),
Hope it helps someone.
Select2 (4.0.3) has an undocumented option: minimumInputLength
This option will prompt the user to fill in the minimum number of characters and then fire the selection
I have a couple of jQuery Ajax requests, which have to be synchronous, but they keep locking/freezing the browser, until the response is received. My main problem is, that until the response is received I have to display a spinning icon, but due to the freezing the spinner is not displayed and even if it miraculously is it doesn't animate.
This is the event displaying the spinner and sending the request:
$(document).on('click', '#open-button', function () {
var input = "some text";
var wrapper = $('#wrapperWindow');
wrapper.children().animate({
opacity: 0
}, 500);
wrapper.children().remove();
wrapper.append('<div id="loading-spinner" style="display:none;"></div>');
var spinner = $('#loading-spinner');
spinner.css({
backgroundImage: 'url("img/loading.gif")',
opacity: 0
});
spinner.show();
spinner.animate({
opacity: 1
}, 500);
var dataForTab = requestData(input); //<-- the request
if (dataForTab.length > 0) {
//do stuff
}
});
The request:
function requestData(input) {
var result = null;
$.ajax({
async: false,
type: "POST",
url: "/some/url?input=" + input,
dataType: "json",
retryLimit: 3,
success: function (json) {
result = json;
},
error: function (xhr, err) {
console.log(xhr);
console.log(err);
}
});
return result;
}
Until the request returns the received JSON data, everything stops moving. How can I fix this please?
That's the essence of synchronous requests, they are locking. You may want to try to move the requests to a web worker. Here's an example (not using XHR, but it can give you an implementation idea)
A web worker is implemented in a separate file, the scripting can look like:
onmessage = function (e) {
var result = null;
$.ajax({
async: false,
type: "POST",
url: "/some/url?input=" + input,
dataType: "json",
retryLimit: 3,
success: function (json) {
result = json;
postMessage({result: result});
},
error: function (xhr, err) {
postMessage({error: err});
}
});
}
Depending on your use case you can use something like
task.js Simplified interface for getting CPU intensive code to run on all cores (node.js, and web)
A example would be
// turn blocking pure function into a worker task
const syncWorkerRequest = task.wrap(function (url) {
// sync request logic
});
// run task on a autoscaling worker pool
syncWorkerRequest('./bla').then(result => {
// do something with result
});
You should not be doing this though, unless you need to do some heavy data processing, please use async requests.