mag*_*n11 27 javascript node.js
我正在映射数组,并且对于新对象的返回值之一,我需要进行异步调用.
var firebaseData = teachers.map(function(teacher) {
return {
name: teacher.title,
description: teacher.body_html,
image: 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
}
});
Run Code Online (Sandbox Code Playgroud)
该函数的实现看起来像
function urlToBase64(url) {
request.get(url, function (error, response, body) {
if (!error && response.statusCode == 200) {
return "data:" + response.headers["content-type"] + ";base64," + new Buffer(body).toString('base64');
}
});
}
Run Code Online (Sandbox Code Playgroud)
我不清楚做到这一点的最佳方法是什么......承诺?嵌套回调?在ES6或ES7中使用一些东西然后用Babel进行转换?
目前实现这一目标的最佳方式是什么?
谢谢!
joe*_*ews 35
一种方法是Promise.all(ES6).
这个答案适用于Node 4.0+.旧版本需要Promise polyfill或库.我还使用了ES6箭头函数,你可以用常规的functions代替Node <4.
这种技术手动包装request.getPromise.您还可以使用像请求承诺这样的库.
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);
})
Run Code Online (Sandbox Code Playgroud)
Kai*_*Kai 29
2018年更新:Promise.all地图回调中的异步函数更容易实现:
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');
}
});
}
Run Code Online (Sandbox Code Playgroud)
编辑@ 2018/04/29:我为大家举了一般例子:
let data = await Promise.all(data.map(async (item) => {
try {
item.fetchItem = await fetchFunc(item.fetchParams);
return item;
} catch(err) {
throw err;
}
});
Run Code Online (Sandbox Code Playgroud)
您可以使用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);
}
Run Code Online (Sandbox Code Playgroud)
请注意,所有教师都将并行处理 - 您也可以使用以下功能:
mapSeries(arr, iterator, [callback]) 一张一张的地图
mapLimit(arr, limit, iterator, [callback])地图limit在同一时间
2020 年,我们现在有了ECMAScript2021的for await...of语法,可以显着简化事情:
所以你现在可以简单地这样做:
//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){
....
}
Run Code Online (Sandbox Code Playgroud)
小智 5
我遇到了类似的问题,发现这更容易(我正在使用 Kai 的通用模板)。下面,您只需要使用一个await. 我还使用 ajax 函数作为我的异步函数:
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)));
Run Code Online (Sandbox Code Playgroud)