There is an array, for each element of which you should call an asynchronous function that returns a value via a callback.

However, it is required to call them sequentially, namely, the next call should be made only when the processing of the previous one has completed.

This is what happens at the same time:

function doSmth(x, callback) { setTimeout(callback, Math.random() * 100 | 0, null, x); } var data = [1, 2, 3, 4, 5, 6, 7, 8]; for (var x of data) { doSmth(x, function (err, res) { console.log(err || res); }); } 
 .as-console-wrapper.as-console-wrapper { max-height: 100vh } 

    2 answers 2

    You have to make a call inside the callback. To do this, you have to rewrite the cycle for recursion ( well, or not quite recursion ):

     function doSmth(x, callback) { setTimeout(callback, Math.random() * 100 | 0, null, x); } var data = [1, 2, 3, 4, 5, 6, 7, 8]; (function go(i) { // <================== рСкурсивная функция вмСсто Ρ†ΠΈΠΊΠ»Π° if (i >= data.length) { return; // <======================== Π²Ρ‹Ρ…ΠΎΠ΄, ΠΊΠΎΠ³Π΄Π° массив закончился } doSmth(data[i], function (err, res) { console.log(err || res); go(i + 1); // <===================== рСкурсивный Π²Ρ‹Π·ΠΎΠ² ΠΈΠ· коллбэка }); })(0); 
     .as-console-wrapper.as-console-wrapper { max-height: 100vh } 

    And one more thing remains - usually we would need to know when the processing was completed. For this, the function go instead of return can call another callback from the calling function:

     function doSmth(x, callback) { setTimeout(callback, Math.random() * 100 | 0, null, x); } function process(data, callback) { (function go(i) { // <================== рСкурсивная функция вмСсто Ρ†ΠΈΠΊΠ»Π° if (i >= data.length) { return callback(null, null); // <=== return позволяСт ΠΈΠ·Π±Π΅ΠΆΠ°Ρ‚ΡŒ рСкурсивного Π²Ρ‹Π·ΠΎΠ²Π° } doSmth(data[i], function (err, res) { if (err) { return callback(err, null); // <== ΠΏΡ€Π΅ΠΊΡ€Π°Ρ‰Π°Π΅ΠΌ Π΄Π°Π»ΡŒΠ½Π΅ΠΉΡˆΡƒΡŽ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΡƒ } console.log(res); go(i + 1); // <===================== рСкурсивный Π²Ρ‹Π·ΠΎΠ² ΠΈΠ· коллбэка }); })(0); } var data = [1, 2, 3, 4, 5, 6, 7, 8]; process(data, function (err, res) { console.log(err || "Π“ΠΎΡ‚ΠΎΠ²ΠΎ!"); }); 
     .as-console-wrapper.as-console-wrapper { max-height: 100vh } 

    • I'm sure there have already been several issues with the sequential execution of asynchronous functions - Grundy
    • @Grundy, I remember this one , but it’s about the other, although it is related to this. - Qwertiy ♦
    • And this one ? - Grundy
    • @Grundy, first, it’s still necessary to find. Secondly, he does not pull on faq. And finally, I wanted to describe exactly the straightening of the cycle in the case when there is already a function with callbacks and you cannot touch it and don’t. - Qwertiy ♦

    In the latest versions of the language there are mechanisms that allow you to write more understandable code than the option with recursion.

    First, you need to convert the function so that it returns the promise (Promise). You do not need to rewrite it: just wrap it.

     function doSmth1(x) { return new Promise((resolve, reject) => { doSmth(x, (err, result) => { if (err) reject(err); else resolve(result); }); }); } 

    You can also write a function once that will wrap any given (or take it from some library):

     function promisify(f) { return function(...args) { return Promise((resolve, reject) => { f.call(this, ...args, (err, result) => { if (err) reject(err); else resolve(result); }); }); } } var doSmth1 = promisify(doSmth); 

    This allows the use of asynchronous functions from the ES2017 standard:

     async function foo() { var doSmth1 = promisify(doSmth); for (var x of data) { console.log(await doSmth1(x)); } } 

    If for some reason there is no desire to allocate an entire function for an asynchronous code, it is not a problem to call an anonymous function in place:

     (async () => { var doSmth1 = promisify(doSmth); for (var x of data) { console.log(await doSmth1(x)); } })(); 

    PS when you write your asynchronous functions, it makes sense to make them "dual-use" - both accept callback and return Promise. This is not difficult, but the ways of writing such functions are beyond the scope of the answer.

    • I would add a snippet to the end ... In general, in my opinion, it was worth doing a separate question with the answer. - Qwertiy ♦
    • @Qwertiy yes no, this is exactly the answer to the question "how to consistently call an asynchronous function with callbacks?" - wrap in industrial goods and use new language tools. The purpose of the answer is to show that the new means of application language do not complicate the code, but simplify it and how to use it in a legacy environment. - Pavel Mayorov