I need a loop that waits for an async call before continuing. Something like:
for ( /* ... */ ) {
someFunction(param1, praram2, function(result) {
//
You can use async await
introduced in ES7:
for ( /* ... */ ) {
let result = await someFunction(param1, param2);
}
alert("For cycle ended");
This works only if someFunction
is returning a Promise!
If someFunction
is not returning a Promise, then you can make it return a Promise by yourself like this:
function asyncSomeFunction(param1,praram2) {
return new Promise((resolve, reject) => {
someFunction(praram1,praram2,(result)=>{
resolve(result);
})
})
}
Then replace this line await someFunction(param1, param2);
by await asynSomeFunction(param1, param2);
Please understand Promises before writing async await
code!
A promise library based solution:
/*
Since this is an open question for JS I have used Kris Kowal's Q promises for the same
*/
var Q = require('q');
/*
Your LOOP body
@success is a parameter(s) you might pass
*/
var loopBody = function(success) {
var d = Q.defer(); /* OR use your favorite promise library like $q in angular */
/*
'setTimeout' will ideally be your node-like callback with this signature ... (err, data) {}
as shown, on success you should resolve
on failure you should reject (as always ...)
*/
setTimeout(function(err, data) {
if (!err) {
d.resolve('success');
} else {
d.reject('failure');
}
}, 100); //100 ms used for illustration only
return d.promise;
};
/*
function to call your loop body
*/
function loop(itr, fn) {
var def = Q.defer();
if (itr <= 0) {
def.reject({ status: "un-successful " });
} else {
var next = loop.bind(undefined, itr - 1, fn); // 'next' is all there is to this
var callback = fn.bind(undefined /*, a, b, c.... */ ); // in case you want to pass some parameters into your loop body
def.promise = callback().then(def.resolve, next);
}
return def.promise;
}
/*
USAGE: loop(iterations, function(){})
the second argument has to be thenable (in other words return a promise)
NOTE: this loop will stop when loop body resolves to a success
Example: Try to upload file 3 times. HURRAY (if successful) or log failed
*/
loop(4, loopBody).then(function() {
//success handler
console.log('HURRAY')
}, function() {
//failed
console.log('failed');
});
If you like wilsonpage's answer but are more accustomed to using async.js's syntax, here is a variation:
function asyncEach(iterableList, callback, done) {
var i = -1,
length = iterableList.length;
function loop() {
i++;
if (i === length) {
done();
return;
}
callback(iterableList[i], loop);
}
loop();
}
asyncEach(['A', 'B', 'C'], function(item, callback) {
setTimeout(function(){
document.write('Iteration ' + item + ' <br>');
callback();
}, 1000);
}, function() {
document.write('All done!');
});
Demo can be found here - http://jsfiddle.net/NXTv7/8/
Also look at this splendid library caolan / async. Your for
loop can easily be accomplished using mapSeries or series.
I could post some sample code if your example had more details in it.
I needed to call some asynchronous function X
times, each iteration must have happened after the previous one was done, so I wrote a litte library that can be used like this:
// https://codepen.io/anon/pen/MOvxaX?editors=0012
var loop = AsyncLoop(function(iteration, value){
console.log("Loop called with iteration and value set to: ", iteration, value);
var random = Math.random()*500;
if(random < 200)
return false;
return new Promise(function(resolve){
setTimeout(resolve.bind(null, random), random);
});
})
.finished(function(){
console.log("Loop has ended");
});
Each time user defined loop function is called, it has two arguments, iteration index and previous call return value.
This is an example of output:
"Loop called with iteration and value set to: " 0 null
"Loop called with iteration and value set to: " 1 496.4137048207333
"Loop called with iteration and value set to: " 2 259.6020382449663
"Loop called with iteration and value set to: " 3 485.5400568702862
"Loop has ended"
Given an asynchronous worker function someFunction
that will call back a result function with a result
argument saying whether or not the loop should continue:
// having:
// function someFunction(param1, praram2, resultfunc))
// function done() { alert("For cycle ended"); }
(function(f){ f(f) })(function(f){
someFunction("param1", "praram2", function(result){
if (result)
f(f); // loop continues
else
done(); // loop ends
});
})
In order to check whether or not to end the loop, the worker function someFunction
can forward the result function to other asynchronous operations. Also, the whole expression can be encapsulated into an asynchronous function by taking a function done
as callback.