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 function
s 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)));