#javascript #promise #output
#javascript #обещание #вывод
Вопрос:
У меня есть пара асинхронных функций, которые получают и сохраняют значение, если оно еще не было предоставлено.
if (!output.valueA) {
output.valueA = await getValueA();
}
if (!output.valueB) {
output.valueB = await getValueB();
}
Я знаю, что могу использовать Promise.all() для ожидания обоих этих вызовов асинхронно, но каков был бы наилучший способ сделать это, не зная, требуется ли вообще тот или иной или оба вызова, а затем сохранить результаты в правильных переменных?
Комментарии:
1. Откуда
output
берется? Или это не имеет отношения к задаче?2. На самом деле это не имеет значения, просто знайте, что ValueA и ValueB могут быть или не быть уже заполнены к моменту вызова этого кода.
3. Знаете ли вы структуру
output
заранее или у нее каждый раз новые свойства?4. Структура выходного объекта известна заранее
Ответ №1:
Вы можете создать массив со всеми обещаниями, отфильтровать его по наличию значения, а затем использовать Promise.all
. Вам также понадобится сопоставление ключей свойств 1 к 1 с асинхронными функциями, которые использовались для получения значения для этого ключа:
const fns = {
"valueA": getValueA,
"valueB": getValueB,
};
const keys = Object.keys(fns);
// all keys
const missing = keys.filter((key) => !output[key]);
// keys with missing values
const promises = missing.map((key) => fns[key]());
// an array of promises
const results = await Promise.all(promises);
// promise results, obviously
for (const [ index, key ] of missing.entries()) { // using Array.prototype.entries
output[key] = results[index];
}
Ответ №2:
Не сильно изменяя свой пример, вы могли бы использовать then
для установки обратного вызова, который устанавливает свойство при разрешении. Сохраните результирующее обещание внутри массива, чтобы вы знали, когда все обещания будут выполнены.
const promises = [];
if (!output.valueA) {
promises.push(getValueA().then(value => output.valueA = value));
}
if (!output.valueB) {
promises.push(getValueB().then(value => output.valueB = value));
}
await Promise.all(promises);
Вы можете использовать более динамичный подход, заранее определив свойства для проверки и их асинхронные средства получения. Примером может быть:
const asyncGetters = [["valueA", getValueA], ["valueB", getValueB]];
await Promise.all(
asyncGetters
.filter(([prop]) => !output[prop])
.map(async ([prop, asyncGetter]) => output[prop] = await asyncGetter())
);
Здесь filter
действует как ваш if
оператор, выбирая только те элементы, которые имеют ложное значение. map
обязательно соберет обещания асинхронных map
обратных вызовов, чтобы вы могли использовать await Promise.all()
для ожидания их завершения.
Комментарии:
1. Вы также можете использовать
promises.push((async () => output.valueA = await getValueA())())
для первого блока кода, однако лично я нахожу асинхронный IIFE немного загадочным в качестве аргумента функции.
Ответ №3:
const output = {
valueA : null,
valueB: null
};
async function getValueA() {
await new Promise(r => setTimeout(r, 3000));
output.valueA = 2;
return output.valueA;
}
async function getValueB() {
await new Promise(r => setTimeout(r, 3000));
output.valueB = 5;
return output.valueB;
}
async function getAllValues() {
const promises = [output.valueA || getValueA(), output.valueB || getValueB()];
await Promise.all(promises);
console.log(output.valueA);
console.log(output.valueB);
}
getAllValues();
Вы можете передать Promise.all
массив с фактическими значениями вместо обещаний, и он просто немедленно разрешится. Нет необходимости проверять, что все они являются обещаниями. Попробуйте запустить мой код выше, но измените output.ValueA и ValueB на число для начала — оно немедленно разрешится.
[править]
прочитайте комментарий OPs и сделал это в иллюстративных целях:
const output = {
valueA : null,
valueB: null
};
async function getValueA() {
await new Promise(r => setTimeout(r, 3000));
return 2;
}
async function getValueB() {
await new Promise(r => setTimeout(r, 3000));
return 5;
}
async function getAllValues() {
const promises = [output.valueA || getValueA(), output.valueB || getValueB()];
const [a, b] = await Promise.all(promises);
output.valueA = a;
output.valueB = b;
console.log(output.valueA);
console.log(output.valueB);
}
getAllValues();
Вы можете использовать деструктурирование массива для получения результатов Promise.all
Комментарии:
1. Для этого требуется переопределять
getValueA
иgetValueB
(и все последующие аналогичные методы получения) для каждого нового значенияoutput
2. Для этого требуется только, чтобы я предположил, что существуют функции, которые существуют в вопросе OPs.
3. Мне нравится этот ответ, но функции
getValueA
иgetValueB
существуют в отдельном модуле, поэтому было бы предпочтительнее устанавливать выходные значения на основе результата функций, а не внутри самих функций.