Question:
How to write simple, clean, maintainable code that runs multiple asynchronous functions in sequence in javascript/jQuery? (when one runs, the other should run)
The following example illustrates my question:
function f1(){ setTimeout( function(){ console.log(1); }, 30); }
function f2(){ setTimeout( function(){ console.log(2); }, 20); }
function f3(){ setTimeout( function(){ console.log(3); }, 10); }
f1(); f2(); f3();
on the output 3 2 1 how to do something that would give out 1 2 3 ?
Preferably without callbacks – because if you need to run more than two functions sequentially, this is already hard to read. There is a lot to say that a solution using the $.Deferred object is possible, but so far I have not seen a reasonable option.
A similar question was asked more than once, but for some reason I did not find an answer that would suit me.
Answer:
If you want to use Promises , you first need to modify your functions to return Promises . For example, the first of your functions would look like:
function f1() {
return new Promise(function(resolve){
setTimeout(function() {
console.log(1);
resolve();
}, 30);
});
}
The rest of the functions are transformed in a similar way.
You now have three functions ( f1
, f2
, f3
) that return Promises and want to execute them sequentially. If you are not using libraries like Bluebird, then you will have to implement the Promise call queue manually. It's not as difficult as it seems:
// Аргумент "deeds" - это массив функций, которые должны выполняться
// последовательно. При этом, каждая функция должна возвращать
// Обещание (Promise).
var seqRunner = function(deeds) {
return deeds.reduce(function(p, deed) {
return p.then(function() {
// Выполняем следующую функцию только после того, как отработала
// предыдущая.
return deed();
});
}, Promise.resolve()); // Инициализируем очередь выполнения.
}
And you need to use this queue like this:
seqRunner([f1, f2, f3]).then(function() {
console.log('Done!');
});
And here is the JSFiddle with a working example .
Comment:
If you have a small number of functions known in advance, then you can do without the seqRunner
function altogether and bind the functions manually:
f1().then(function() {
return f2();
}).then(function() {
return f3();
}).then(function() {
console.log('Done!');
});