I have the following block of code:
(function() {
QUnit.config.testTimeout = 10000;
var stringformat = QUnit.stringformat;
module('Web API GET Result has expected shape');
asyncTest('HomeData should return an array of Sets with their info as well as cards with their info, but no sides',
function () {
$.ajax({
url: '/sample/url',
dataType: 'json',
success: function (result) {
ok(
!!result.item, 'Got something');
start();
},
error: function(result) {
ok(false, 'Failed with: ' + result.responseText);
start();
}
}
);
}
);
return function () { asyncTest(); };
}
I'm trying to run this for multiple URLs with different results, but I can't figure out how to run asyncTest twice with different parameters. I tried assigning the function (second parameter of asyncTest) to a variable and inserting it into asyncTest() however this doesn't work. How can I define multiple "asyncTest"s and run them?
$.deferred(), $.when() and $.pipe() are your friends if you use jQuery [apparent from the code].
Good tutorials on the above are here, here
Related
I'm facing an issue with deferred usage where 2 nested function that should wait for each other actually run in the wrong order silently.
I cant' figure out where I mix return promise.
So here is what I try to achieve. In a mobile Cordova app, when user enter the Game view, I got a function that download question in WebSql, and I want then to retrieve one question, and then my slider function load the content.
So I nested the getQuestion function in the .done() event.
router.addRoute('game', function () {
'use strict';
//Reload Question List when User enter the Game view.
questionService.initialize().done(
//Now we got question, initialize the Game View
questionService.getQuestion().done(
function (data) {
console.log(data);
slider.slidePage(new GameView(data).render().$el);
})
);
Here is how I use the $.Deferred() in both function. First I declare my $.Deferred() and at the end of the function I return the promise.
But my getQuestion() in code below does not wait for the initialize() function to end before start.
Where did I mixed up my promise return?
var getQuestions = function(param) {
var deferred = $.Deferred();
param = param;
$.ajax({
type: 'POST',
url: 'myserver',
data: {
region: uRegion
},
success: function(value, status) {
//do something with value
this.db = window.openDatabase('database details');
this.db.transaction(function(tx) {
storeQuestion(tx);
}, function(error) {
deferred.reject('Transaction error: ' + error);
}, function() {
//Transaction success
deferred.resolve();
});
},
error: function(textStatus, exception) {}
});
return deferred.promise();
};
You are not passing the success callback handler. As per current implementation your are invoking getQuestion() immediately.
Use anonymous function, rest its fine
//Reload Question List when User enter the Game view.
questionService.initialize().done(function(){
//Now we got question, initialize the Game View
questionService.getQuestion().done(
function (data) {
console.log(data);
slider.slidePage(new GameView(data).render().$el);
})
});
I have a javascript code which have to request the database (ajax). But I discovered that the inserts were wrong but with the right sql request. So I added an alert on which ajax request to know when the code is executed.
Here is the code :
$.post("/kohana-v3.3.5/ajax/update_simulation", {
id_simulation: id_simulation,
nom_simulation: nom_simulation,
sol_simulation: sol_simulation,
station_simulation: station_simulation,
iteration_simulation: iteration_simulation,
scenario_simulation: scenario_simulation
}
, function (result) {
console.log(result);
alert('update');
});
$.post("/kohana-v3.3.5/ajax/delete_pousses", {id_simulation: id_simulation}, function (result) {
console.log(result);
alert('delete');
});
$(this).prev('div').find('table .formRows').each(function (i) {
alert('here');
if (cpt % 2 == 1) {
//interculture
var $tds = $(this).find('td option:selected'),
culture = $tds.eq(0).val(),
date = $tds.eq(1).text();
itk = null;
} else {
//culture
var $tds = $(this).find('td option:selected'),
culture = $tds.eq(0).val(),
itk = $tds.eq(1).val();
date = null;
}
$.post("/kohana-v3.3.5/ajax/insert_pousses", {
id_simulation: id_simulation,
culture: culture,
date: date,
itk: itk,
rang: cpt
}, function (result) {
console.log(result);
alert('insert');
}); //Fin du post
cpt++;
}); //Fin du each
Each time I run that code, the order of the alert is always different ! Sometimes "insert update delete", sometimes "update, delete insert" ...
And it's a problem because if the delete is the last one, the insert will be removed. So, is it a normal way ? How should I resolve it ?
javascript can be executed asynchronously - and that's the reason why your ajax requests are not always executed in the same order. You can set them asnyc false (like here jQuery: Performing synchronous AJAX requests) or make something like promises (https://api.jquery.com/promise/) to wait for the ajax call to be finished.
greetings
AJAX requests are asynchronous, so you cannot guarantee an order if you trigger them as siblings like this.
In order to guarantee a fixed order, you need to make the subsequent call from the success block of its predecessor. Something like this:
$.post('/ajax/method1', { params: params },
function(result) {
$.post('/ajax/method2', { params: params },
function(result) {
$.post('/ajax/method3', { params: params },
function(result) {
});
});
});
You can use .promise to "observe when all actions of a certain type bound to the collection, queued or not, have finished."
https://api.jquery.com/promise/
Example Function
function testFunction() {
var deferred = $.Deferred();
$.ajax({
type: "POST",
url: "",
success: function (data) {
deferred.resolve(data);
}
});
return deferred.promise();
}
Calling Function
function CallingFunction()
{
var promise = testFunction();
promise.then(function (data) {
//do bits / call next funtion
}
}
Update
This may also help you out:
"Register a handler to be called when all Ajax requests have completed."
https://api.jquery.com/ajaxStop/
$(document).ajaxStop(function () {
});
Final note:
As of jQuery 1.8, the use of async: false is deprecated, use with $.Deferred.
you need to call post ajax method after by the success of previous one.
like:
$.post("/kohana-v3.3.5/ajax/update_simulation", {
id_simulation: id_simulation,
nom_simulation: nom_simulation,
sol_simulation: sol_simulation,
station_simulation: station_simulation,
iteration_simulation: iteration_simulation,
scenario_simulation: scenario_simulation
}
, function (result) {
console.log(result);
alert('update');
dleteajax();
});
function dleteajax()
{
$.post("/kohana-v3.3.5/ajax/delete_pousses", {id_simulation: id_simulation}, function (result) {
console.log(result);
alert('delete');
});
}
I am facing the following synchronization issue. I wouldn't be surprised if it has a simple solution/workaround. The BuildMenu() function is called from another block of code and it calls the CreateMenuData() which makes a request to a service which return some data. The problem is that since it is an async call to the service when the data variable is being used it is undefined. I have provided the js log that also shows my point.
BuildMenu: function () {
console.log("before call");
var data=this.CreateMenuData();
console.log("after call");
//Doing more stuff with data that fail.
}
CreateMenuData: function () {
console.log("func starts");
data = [];
dojo.forEach(config.layerlist, function (collection, colindex) {
var layersRequest = esriRequest({
url: collection.url,
handleAs: "json",
});
layersRequest.then(
function (response) {
dojo.forEach(response.records, function (value, key) {
console.log(key);
data.push(key);
});
}, function (error) {
});
});
console.log("func ends");
return data;
}
Console log writes:
before call
func starts
func ends
after call
0
1
2
3
4
FYI: using anything "dojo." is deprecated. Make sure you are pulling all the modules you need in "require".
Ken has pointed you the right direction, go through the link and get familiarized with the asynchronous requests.
However, I'd like to point out that you are not handling only one async request, but potentionally there might be more of them of which you are trying to fill the "data" with. To make sure you handle the results only when all of the requests are finished, you should use "dojo/promise/all".
CreateMenuData: function (callback) {
console.log("func starts");
requests = [];
data = [];
var scope = this;
require(["dojo/_base/lang", "dojo/base/array", "dojo/promise/all"], function(lang, array, all){
array.forEach(config.layerlist, function (collection, colindex) {
var promise = esriRequest({
url: collection.url,
handleAs: "json",
});
requests.push(promise);
});
// Now use the dojo/promise/all object
all(requests).then(function(responses){
// Check for all the responses and add whatever you need to the data object.
...
// once it's all done, apply the callback. watch the scope!
if (typeof callback == "function")
callback.apply(scope, data);
});
});
}
so now you have that method ready, call it
BuildMenu: function () {
console.log("before call");
var dataCallback = function(data){
// do whatever you need to do with the data or call other function that handles them.
}
this.CreateMenuData(dataCallback);
}
I currently have the following code:
function render(url1, url2, message) {
utility.messageBoxOpen(message);
$.getJSON(url1, function (items) {
// Do something
utility.messageBoxClose();
});
$.getJSON(url2, function (items) {
// Do something
});
}
When the function is executed a modal window appears to inform the user something is loading. Initially I only had one $getJSON request so when the request was done the modal window closed as per the code above.
I am looking to add another $getJSON request but want to close the modal window only when both $getJSON requests have completed.
What is the best way of achieving this?
You're looking for $.when()
All jQuery ajax requests (including shortcuts like getJSON) return deferred objects which can be used to control other actions.
var dfd1 = $.getJSON(url1, function (items) {
// Do something
});
var dfd1 = $.getJSON(url2, function (items) {
// Do something
});
$.when(dfd1, dfd2).then(function(){
//both succeeded
utility.messageBoxClose();
},function(){
//one or more of them failed
});
If you don't care whether the getJSONs come back successfully or not and instead only care that they are done you can instead:
$.when(dfd1, dfd2).done( utility.messageBoxClose );
A variable
function render(url1, url2, message) {
utility.messageBoxOpen(message);
var isOneDone = false;
$.getJSON(url1, function (items) {
// Do something
if(!isOneDone)
isOneDone = true;
else
utility.messageBoxClose();
});
$.getJSON(url2, function (items) {
// Do something
if(!isOneDone)
isOneDone = true;
else
utility.messageBoxClose();
});
}
You can replace the getJSON() call to one using $.ajax which accomplishes the same thing but gives you more flexibility:
$.ajax({
url: http://whatever,
dataType: 'json',
async: false,
data: {data},
success: function(data) {
// do the thing
}
});
Note the async:false part - this makes the code execution pause until the request is completed. So you could simply make your two calls this way, and close the dialog after the second call is completed.
function render(url1, url2, message) {
utility.messageBoxOpen(message);
$.when($.getJSON(url1, function (items) {
// Do something
utility.messageBoxClose();
}), $.getJSON(url2, function (items) {
// Do something
})).then(function () {
//Both complete
});
}
jQuery.when
Hi the below Javascript is called when I submit a form. It first splits a bunch of url's from a text area, it then:
1) Adds lines to a table for each url, and in the last column (the 'status' column) it says "Not Started".
2) Again it loops through each url, first off it makes an ajax call to check on the status (status.php) which will return a percentage from 0 - 100.
3) In the same loop it kicks off the actual process via ajax (process.php), when the process has completed (bearing in the mind the continuous status updates), it will then say "Completed" in the status column and exit the auto_refresh.
4) It should then go to the next 'each' and do the same for the next url.
function formSubmit(){
var lines = $('#urls').val().split('\n');
$.each(lines, function(key, value) {
$('#dlTable tr:last').after('<tr><td>'+value+'</td><td>Not Started</td></tr>');
});
$.each(lines, function(key, value) {
var auto_refresh = setInterval( function () {
$.ajax({
url: 'status.php',
success: function(data) {
$('#dlTable').find("tr").eq(key+1).children().last().replaceWith("<td>"+data+"</td>");
}
});
}, 1000);
$.ajax({
url: 'process.php?id='+value,
success: function(msg) {
clearInterval(auto_refresh);
$('#dlTable').find("tr").eq(key+1).children().last().replaceWith("<td>completed rip</td>");
}
});
});
}
What you want is to run several asynchronous actions in sequence, right? I'd build an array of the functions to execute and run it through a sequence helper.
https://github.com/michiel/asynchelper-js/blob/master/lib/sequencer.js
var actions = [];
$.each(lines, function(key, value) {
actions.push(function(callback) {
$.ajax({
url: 'process.php?id='+val,
success: function(msg) {
clearInterval(auto_refresh);
//
// Perform your DOM operations here and be sure to call the
// callback!
//
callback();
}
});
});
}
);
As you can see, we build an array of scoped functions that take an arbitrary callback as an argument. A sequencer will run them in order for you.
Use the sequence helper from the github link and run,
var sequencer = new Sequencer(actions);
sequencer.start();
It is, btw, possible to define sequencer functions in a few lines of code. For example,
function sequencer(arr) {
(function() {
((arr.length != 0) && (arr.shift()(arguments.callee)));
})();
}
AJAX is asynchronous.
That's exactly what's supposed to happen.
Instead of using each, you should send the next AJAX request in the completion handler of the previous one.
You can also set AJAX to run synchronously using the "async" property. Add the following:
$.ajax({ "async": false, ... other options ... });
AJAX API reference here. Note that this will lock the browser until the request completes.
I prefer the approach in SLaks answer (sticking with asynchronous behavior). However, it does depend on your application. Exercise caution.
I would give the same answer as this jquery json populate table
This code will give you a little idea how to use callback with loops and ajax. But I have not tested it and there will be bugs. I derived the following from my old code:-
var processCnt; //Global variable - check if this is needed
function formSubmit(){
var lines = $('#urls').val().split('\n');
$.each(lines, function(key, value) {
$('#dlTable tr:last').after('<tr><td>'+value+'</td><td>Not Started</td></tr>');
});
completeProcessing(lines ,function(success)
{
$.ajax({
url: 'process.php?id='+value,
success: function(msg) {
$('#dlTable').find("tr").eq(key+1).children().last().replaceWith("<td>completed rip</td>");
}
});
});
}
//Following two functions added by me
function completeProcessing(lines,callback)
{
processCnt= 0;
processingTimer = setInterval(function() { singleProcessing(lines[processCnt],function(completeProcessingSuccess){ if(completeProcessingSuccess){ clearInterval(processingTimer); callback(true); }})}, 1000);
}
function singleProcessing(line,callback)
{
key=processCnt;
val = line;
if(processCnt < totalFiles)
{ //Files to be processed
$.ajax({
url: 'status.php',
success: function(data) {
$('#dlTable').find("tr").eq(key+1).children().last().replaceWith("<td>"+data+"</td>");
processCnt++;
callback(false);
}
});
}
else
{
callback(true);
}
}