Лучший способ вызова асинхронной функции внутри карты?
Я сопоставляю массив и для одного из возвращаемых значений нового объекта, мне нужно сделать асинхронный вызов.
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
}
});
Реализация этой функции будет выглядеть примерно так:
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');
}
});
}
Я не знаю, какой лучший подход для этого... promises? Вложенные обратные вызовы? Использовать что-то в ES6 или ES7, а затем перетащить с помощью Babel?
Каков наилучший способ реализации этого?
Спасибо!
Ответы
Ответ 1
Одним из подходов является Promise.all
(ES6).
Этот ответ будет работать в Узле 4. 0+. В старых версиях потребуется полифилл или библиотека Promise. Я также использовал функции стрелок ES6, которые можно заменить обычной function
для узла <4.
Этот метод вручную оборачивает request.get
Обещанием. Вы также можете использовать библиотеку, как запрос-обещание.
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);
})
Ответ 2
обновление в 2018: Promise.all
функцию 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');
}
});
}
Edit @2018/04/29: я поставил общий пример для всех:
Edit @2019/06/19: async/await должен иметь try/catch для обработки ошибки, в противном случае выдается предупреждение;
let data = await Promise.all(data.map(async (item) => {
try {
item.fetchItem = await fetchFunc(item.fetchParams);
return item;
} catch(err) {
throw err;
}
});
Ответ 3
Вы можете использовать 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);
}
обратите внимание, что все учителя будут обрабатываться параллельно - вы также можете использовать следующие функции:
mapSeries(arr, iterator, [callback])
отображается один за другим
mapLimit(arr, limit, iterator, [callback])
отображает limit
в то же время
Ответ 4
2019
предупреждение с Promise.all, они не выполняются в точном порядке, некоторые серверы не могут поддерживать одновременное выполнение запросов или API-вызовов.
Более новые библиотеки делают мои одностраничные приложения более 4 мегапикселями. Поэтому я решил не добавлять больше новых библиотек, таких как lodash и т.д., Которые можно заменить кодом. Я хорошие решения для использования ждут с картой:
rows.map ( ( record ) => {
try {
(async () => {
let col = await client.query('SELECT * FROM customers');
})();
} catch (err) {
console.log(err);
}
});
Ответ 5
Я использую асинхронную функцию над массивом. И не использовать array.map, а для функции. Это что-то вроде этого:
const resultingProcessedArray = async function getSomeArray() {
try {
let { data } = await axios({url: '/myUrl', method:'GET'}); //initial array
let resultingProcessedArray = [];
for (let i = 0, len = data.items.length; i < len; i++) {
let results = await axios({url: `/users?filter=id eq ${data.items[i].someId}`, method:'GET'});
let domainName = results.data.items[0].domainName;
resultingProcessedArray.push(Object.assign(data.items[i], {domainName}));
}
return resultingProcessedArray;
} catch (err) {
console.error("Unable to fetch the data", err);
return [];
}
};
Ответ 6
Я должен был написать это, для удобства. В противном случае мне может понадобиться https://github.com/mcollina/make-promises-safe
export async function mapAsync<T, U>(
arr: T[],
callbackfn: (value: T, index: number, array: T[]) => Promise<U>,
thisArg?: any
) {
return await Promise.all(arr.map(async (value, index, array) => {
try {
return await callbackfn(value, index, array);
} catch(e) {
throw e;
}
}, thisArg));
}