Best way to call an asynchronous function within map?

update in 2018: Promise.all async function within map callback is easier to implement:

    let firebaseData = await Promise.all(teachers.map(async teacher => {
        return {
            name: teacher.title,
            description: teacher.body_html,
            image: await urlToBase64(teacher.summary_html.match(/src="(.*?)"/)[1]),
            city: metafieldTeacherData[teacher.id].city,
            country: metafieldTeacherData[teacher.id].country,
            state: metafieldTeacherData[teacher.id].state,
            studioName: metafieldTeacherData[teacher.id].studioName,
            studioURL: metafieldTeacherData[teacher.id].studioURL
        }
    }));


async function urlToBase64(url) {
  return request.get(url, function (error, response, body) {
    if (!error && response.statusCode == 200) {
      return "data:" + response.headers["content-type"] + ";base64," + new Buffer(body).toString('base64');
    }
  });
}

Edit@2018/04/29: I put the general example for everyone:

Edit@2019/06/19 : async/await should have try/catch to handle error, if not it would throw an warning message;

let data = await Promise.all(data.map(async (item) => {
      try {
      item.fetchItem = await fetchFunc(item.fetchParams);

      return item; 
      } catch(err) {
         throw err;
      }
  }));

One approach is Promise.all (ES6).

This answer will work in Node 4.0+. Older versions will need a Promise polyfill or library. I have also used ES6 arrow functions, which you could replace with regular functions for Node < 4.

This technique manually wraps request.get with a Promise. You could also use a library like request-promise.

function urlToBase64(url) {
  return new Promise((resolve, reject) => {
    request.get(url, function (error, response, body) {
      if (!error && response.statusCode == 200) {
        resolve("data:" + response.headers["content-type"] + ";base64," + new Buffer(body).toString('base64'));
      } else {
        reject(response);
      }
    });
  })
} 

// Map input data to an Array of Promises
let promises = input.map(element => {
  return urlToBase64(element.image)
    .then(base64 => {
      element.base64Data = base64;
      return element;
    })
});

// Wait for all Promises to complete
Promise.all(promises)
  .then(results => {
    // Handle results
  })
  .catch(e => {
    console.error(e);
  })

In 2020 we now have the for await...of syntax of ECMAScript2021 that significantly simplifies things:

So you can now simply do this:

//return an array of promises from our iteration:
let promises = teachers.map(async m => {
   return await request.get(....);
});

//simply iterate those
//val will be the result of the promise not the promise itself
for await (let val of promises){
   ....
}

You can use async.map.

var async = require('async');

async.map(teachers, mapTeacher, function(err, results){
  // results is now an array of stats for each file
});

function mapTeacher(teacher, done) {
  // computing stuff here...
  done(null, teacher);
}

note that all teachers will be processed in parallel - you can use also this functions:

mapSeries(arr, iterator, [callback]) maps one by one

mapLimit(arr, limit, iterator, [callback]) maps limit at same time


I had a similar problem and found this to be easier (I'm using Kai's generic template). Below, you only need to use one await. I was also using an ajax function as my async function:

function asyncFunction(item) {
    return $.ajax({
        type: "GET",
        url: url,
        success: response => {
            console.log("response received:", response);
            return response;
        }, error: err => {
            console.log("error in ajax", err);
        }
    });
}

let data = await Promise.all(data.map(item => asyncFunction(item)));