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.