Increment for only after the previous interaction has been finished (callback) - javascript

I'm having a problem with callback functions in javascript. What I want to do is: loop on a for and call a function passing i as parameter. With that in mind, I have to loop to the next interaction only after the previous one has been finished. I don't know if this is a problem but inside the function I'm sending i as parameter, I have another callback function. Here is my code:
for(i=0; i<10; i++) {
aux(i, function(success) {
/*
* this should be made interaction by interaction
* but what happens is: while I'm still running my first interaction
* (i=0), the code loops for i=1, i=2, etc. before the response of
* the previous interaction
*/
if(!success)
doSomething();
else
doSomethingElse();
});
}
function aux(i, success) {
... //here I make my logic with "i" sent as parameter
getReturnFromAjax(function(response) {
if(response)
return success(true);
else
return success(false);
});
});
function getReturnFromAjax(callback) {
...
$.ajax({
url: myUrl,
type: "POST",
success: function (response) {
return callback(response);
}
});
}

jQuery's Deferred can be a bit tricky to get right. What you'll have to do is stack your promises in a chain. For example:
var
// create a deferred object
dfd = $.Deferred(),
// get the promise
promise = dfd.promise(),
// the loop variable
i
;
for(i = 0; i < 10; i += 1) {
// use `then` and use the new promise for next itteration
promise = promise.then(
// prepare the function to be called, but don't execute it!
// (see docs for .bind)
aux.bind(null, i, function(success) {
success ? doSomethingElse() : doSomething();
})
);
}
// resolve the deferred object
dfd.resolve();
for this to work, aux must also return a promise, but $.ajax already does this, so just pass it through and everything should work:
in aux:
function aux(i, callback) {
console.log('executing for `aux` with', i);
// return the ajax-promise
return getReturnFromAjax(function(response) {
callback(Boolean(response));
});
}
in getReturnFromAjax:
function getReturnFromAjax(callback) {
// return the ajax-promise
return $.ajax({
url: '%your-url%',
type: '%method%',
success: function (response) {
callback(response);
}
});
}
demo: http://jsbin.com/pilebofi/2/

I'd suggest that you'd look into jQuery's Deferred Objects and jQuery.Deferred()-method instead of making your own callback queue functions (as you are already using jQuery anyway).
Description: A constructor function that returns a chainable utility
object with methods to register multiple callbacks into callback
queues, invoke callback queues, and relay the success or failure state
of any synchronous or asynchronous function.

I don't have experience with jQuery, but your callback looks a bit fishy to me.
In plain JS I'd suggest trying something among the lines of this:
function yourMainFunction
{
function callbackHandler(result)
{
// Code that depends on on the result of the callback
}
getAjaxResults(callbackHandler);
}
function getAjaxResults(callbackHandler)
{
// Create xmlHttpRequest Handler, etc.
// Make your AJAX request
xmlHttp.onreadystatechange = function()
{
if (xmlHttp.readyState == 4 && xmlHttp.status==200)
{
// Do stuff you want to do if the request was successful
// Define a variable with the value(s) you want to return to the main function
callbackHandler(yourReturnVariable);
}
}
}

Related

use a callback function before a .done() function in ajax call promises

Here is schematically what I am trying to achieve :
function loadLotsofSomething() {
for (var i=0;i<1000;i++) {
results.push(loadOneSomething(i))
}
$.when.apply(this,results).done(function() {
for (var i = 0; i < arguments.length; i++) {
//get doSomehting(data) instead of data here
}
}
}
function loadOneSomething(i) {
return $.ajax({
async : true,
url: url,
success: function(data){
return doSomething(data);
}
});
}
function doSomething (x) {
return x;
}
I would want the success function to execute before the done function because It's simpler to modify the data I get from the ajax call before looping through all the calls.
In my case, no matter what I put into the success function, I always get the raw data in the done function.
Any help welcome !
You want to use chaining (as is often the case with promises). Don't use success at all; instead, use then and return its result:
function loadOneSomething(i) {
return $.ajax({
async : true,
url: url
}).then(function(data){
return doSomething(data);
});
}
That way, the promise returned by loadOneSomething is the one from then, not the one from $.ajax, and its resolution value is what you return from then.
More: The jqXHR entry in $.ajax, deferred.then.

WinJS : Returning promise from a function

I would like to make a function that returns a promise. That promise would contain the data of an asynchronous call made in the function. What I want it to look like :
//Function that do asynchronous work
function f1() {
var url = ...
WinJS.xhr({ url: url }).then(
function completed(request) {
var data = ...processing the request...
...
},
function error(request) {
...
});
}
//Code that would use the result of the asynchronous function
f1().done(function(data) {
...
});
The only way I found to make this work is to pass a callback to f1 and call it when I have the data. Using callbacks though seems to defeat the goal achieved by promises. Is there a way to make it work like above? Also, I could return WinJS.xhr in f1, but the done method of f1 would return the request and not the "data".
There's little to change:
function f1() {
var url = …;
return WinJS.xhr({ url: url }).then(function completed(request) {
// ^^^^^^
var data = …; // processing the request
return data;
// ^^^^^^^^^^^
});
}
//Code that would use the result of the asynchronous function
f1().done(function(data) {
…
}, function error(request) {
… // better handle errors in the end
});
You don't want to return the WinJS.xhr() itself indeed, but you want to return the result of the .then(…) call which is exactly the promise that resolves with the return value of the callback. This is one of the main features of promises :-)

chaining jquery .when().then() in a loop with fixed end of chain call

The closest answer I could find was this https://stackoverflow.com/a/17216555/2834734
The most common use for .then is chaining ajax requests:
$.ajax({...}).then(function(){
return $.ajax({...}); }).then(function(){
return $.ajax({...}); }).then(function(){
return $.ajax({...}); }).then(function(){
return $.ajax({...}); });
this can easily be done in a loop
However it's the looping procedure I'm having difficulty with plus I have some unusual circumstances.
A brief explanation is, I have an array of requests that I need to loop through, some will invoke an ajax load and others will not. I need them to run consecutively but also run a specific function call at then end.
Here is a simple(I hope) sample of my situation:
// Here is my flow of logic
var thingsToDo = new tasks(); // Initiate the constructor, explained below
// Loop through the requests array and process them consecutively
for (var i in thingsToDo.requests) {
$.when(thingsToDo.jqxhr).then(function() {
thingsToDo.requests[i].fn();
})
}
// Run my final function at the end of the chain.
$.when(thingsToDo.jqxhr).then(function() {
runOnceAllComplete();
});
This is the constructor class the above is based on.
// Constructor
function tasks() {
_tasks_ = this; // automatic global var
this.jqxhr = undefined; // Var to monitor ajax calls
this.requests = [ // list of tasks to run.
{
url: 'file1.php',
fn: function() {
_tasks_.load(this.url);
console.log('file1 loaded');
}
}, {
url: 'file2.php',
fn: function() {
_tasks_.load(this.url);
console.log('file2 loaded');
}
}, {
noUrl: true, // Note there is no file to load here
fn: function() {
console.log('no file here to load, but process something else');
$('body').css("background-color", "blue");
}
}, {
url: 'file3.php',
fn: function() {
_tasks_.load(this.url);
console.log('file1 loaded');
}
},
];
this.load = function(file) { // This runs the ajax call and resets this.jqxhr
this.jqxhr = $.get(file);
}
}
function runOnceAllComplete() {
alert('hooray!, we finished');
}
The tricky part I have is the requests are created dynamically so there can be 1-n many requests to perform, which is why I chose to loop, and they must be performed in that order.
As mentioned some requests will invoke an ajax call and others may not, this doesn't seem to break $.when().then(), but the problem is the loop continues before the promise is resolved and my final function happens before the final request.
Still trying to get my head around promises, the first time I've used them.
Try including return statement at fn , this.load ; adding .promise() chained to $("body") at fn to return a jQuery promise object ; using Function.prototype.apply() , $.map() at $.when()
fn: function() {
// added `return`
return _tasks_.load(this.url);
}
this.load = function(file) {
this.jqxhr = $.get(file);
// added `return`
return this.jqxhr
}
fn: function() {
console.log('no file here to load, but process something else');
// added `return` , `.promise()`
return $('body').css("background-color", "blue").promise();
}
$.when.apply($, $.map(thingsToDo.requests, function(task) {
return task.fn()
})).then(runOnceAllComplete)
See also Pass in an array of Deferreds to $.when() , What does $.when.apply($, someArray) do?
however I'm encountering a problem, using the .map() it doesn't wait
for each request to complete before processing the next one. I need
each one to complete before moving to the next.
Try using .queue() , which will calls functions in queue sequentially, and only when next is called at current function
$(thingsToDo).queue("tasks", $.map(thingsToDo.requests, function(task) {
return function(next) {
// call next function in `"tasks"` queue
// when current function completes using `.then(next)`
return task.fn().then(next)
}
})).dequeue("tasks").promise("tasks").then(runOnceAllComplete)
See .queue() , .promise() , Execute function queue in javascript

Best Practices to wait for multiple calls

I have this code as a starting point.
// $ = jQuery
// groupAdata and groupBdata are arrays
function funcA(elem) {
for (f = 0; f < groupAdata.length ; f++) {
// this is an example on how this function calls other functions asynchronously.
elem.children('.partyA').each( function() {
this.innerHTML = "been here" + groupAdata[f];
});
}
}
function funcB(elem) {
// another function that fires more calls
for (f = 0; f < groupAdata.length ; f++) {
$.post(url, somedata, function(data) {
elem.children('.partyB').each( function() {
this.innerHTML = "will be there" + groupBdata[f] + data;
});
}
}
}
$(document).ready(function() {
$('.groupA').each(function () {
funcA(this);
});
$('.groupB').each(function (){
funcB(this);
});
});
function endofitall() {
// call this after all instances of funcA and funcB are done.
}
When running endofitall(), I'd like to be sure that all calls of funcA and funcB are done.
I take that Promises and jQuery.Deferred() would be a good/preferred approach but was not able to map the answers I found to this specific scenario. (It is part of a templating tool that fires multiple dom manipulators func[AB] for multiple DOM elements.)
You can use $.when().
Your goal should be to get to:
// call funcA, call funcB
$.when( funcA(), funcB() )
// when everything is done go on with the callback
.done(endofitall);
In the case of funcA (synchronous function there's no problem and it will work as is).
In the case of funcB (asynchronous) there are some things to consider. If it would be just one ajax call your code should be something like:
// This function returns a promise.
// When it's fulfilled the callback (in your case '.done(endofitall)')
// will be called.
function funcB(somedata){
return $.post(url, somedata);
}
As you are actually making more requests you have to return a resolved promise only when all calls have been fulfilled.
// an *Asynchronous* function, returns an array of promises
function funcB(elem, groupAdata) {
var allCalls = [];
// for each element in the array call the relative async
// function. While you're calling it push it to the array.
groupAdata.forEach(data, function(data){
allCalls.push( $.post(url, data) );
});
// allCalls is now an array of promises.
// why .apply(undefined)? read here: https://stackoverflow.com/a/14352218/1446845
return $.when.apply(undefined, allCalls);
}
At this point you can go for a flat and clear:
$.when( funcA(), funcB() ).done(endofitall);
As a rule of thumb: if you are making async requests try to always return a promise from them, this will help flatten out your code (will post some link later on if you want) and to leverage the power of callbacks.
The above code can surely be refactored further (also, I haven't used a lot of jQuery in the last few years, but the concept applies to any Js library or even when using no library at all) but I hope it will help as a starting point.
References:
$.when
A similar answer here on SO
Call endofitall() inside each iteration for funcA and funcB. Keep a counter and perform the actual work once the counter reaches the number signifying all the tasks are complete.
function funcA(elem) {
for (f = 0; f < groupAdata.length ; f++) {
// these calls are not async
elem.children('.partyA').each( function() {
this.innerHTML = "been here" + groupAdata[f];
});
endofitall();
}
}
function funcB(elem) {
// another function that fires more calls
for (f = 0; f < groupBdata.length ; f++) {
$.post(url, somedata, function(data) {
elem.children('.partyB').each( function() {
this.innerHTML = "will be there" + groupBdata[f] + data;
});
endofitall();
}
}
}
$(document).ready(function() {
$('.groupA').each(function () {
funcA(this);
});
$('.groupB').each(function (){
funcB(this);
});
});
var counter=0;
function endofitall() {
if(++counter==groupAdata.length + groupBdata.length){
//do stuff
}

Chain queuing callbacks results

I got a loop like this:
for ( var current in all )
{
//load the item
prepare.load( all[current].resource , function( result ) {
doSomethingWithResult(result);
});
}
function AllItemsLoaded()
{
}
My goal is to execute AllItemsLoaded() after all items are loaded and the code in the callbacks is executed, e.g. For every item callback should be called and DoSomethingWithResult() should be executed before AllItemsLoaded() is called, all these items are loaded asynchronously.
I've tried Jquery Deferred/pipe and my code looked like this:
var chain = new $.Deferred().resolve();
for ( var current in all )
{
chain = chain.pipe(function(res){
prepare.load( all[current].resource , function( result ) {
doSomethingWithResult(result);
});
});
//if I do a return here, the pipe will continue without getting the result,
so I need to continue the pipe after load's callback and
doSomethingWithResult is executed
}
chain.done(AllItemsLoaded);
Deferred is a good idea. However, you need to wait on the promise. Here's a method using when to wait on all the promises without doing them in order:
var loads = [];
for ( var current in all )
{
(function(){
var deferred = new $.Deferred();
prepare.load( all[current].resource , function( result ) {
doSomethingWithResult(result);
deferred.resolve(result);
});
loads.push(deferred.promise());
})();
}
$.when.apply(null, loads).then(AllItemsLoaded);
First create a new deferred for each load. Place its promise in a collection. After the load, resolve the deferred. Wait on all of the loads with $.when().
Is this what you need?
From: http://aabs.wordpress.com/2009/12/16/sequential-script-loading-on-demand/
function LoadScriptsSequentially(scriptUrls, callback)
{
if (typeof scriptUrls == 'undefined') throw "Argument Error: URL array is unusable";
if (scriptUrls.length == 0 && typeof callback == 'function') callback();
$.getScript(scriptUrls.shift(), function() { LoadScriptsSequentially(scriptUrls, callback); });
}
I would approach it as such (below), where you replace each $.get() with your own asynchronous object, with it's own individual complete handler.
$(document).ready(function() {
$.when(
$.get("ajax.php?a=b"),
$.get("ajax.php?a=c"),
$.get("ajax.php?a=d")
).then(
function() {
// both AJAX calls have succeeded
alert("All Done");
},
function() {
// one of the AJAX calls has failed
alert("One or more failed");
}
);
});
First thing is to use .get() or .post() not .load(), the reason being that .load() returns jQuery while the other two return jqXHR (ie a promise), which is what you want here.
Next thing is to provide an array in which to accumulate the jqXHR promises.
Last you need to know how to get $.when() to act on the array of promises, to do something when all of them are resolved (or an error occurs).
The whole thing looks like this :
var promises = [];//new Array
for ( var current in all ) {
prepare.get( all[current].resource, function( result ) {
doSomethingWithResult(result);
});
}
$.when.apply(null, promises).then(AllItemsLoaded, myErrorHandler);

Categories

Resources