Ответ 1
TL; DR
Не используйте шаблон в вопросе, где вы получаете обещания, а затем отдельно ждите их; вместо этого используйте Promise.all
(по крайней мере, пока):
const [value1, value2] = await Promise.all([getValue1Async(), getValue2Async()]);
Хотя ваше решение выполняет две операции параллельно, оно не обрабатывает отклонение должным образом, если оба обещания отклоняются.
Подробности:
Ваше решение запускает их параллельно, но всегда ждет завершения первого, а затем ожидания второго. Если вы просто хотите запустить их, запустить их параллельно и получить оба результата, это нормально. (Нет, это не так, продолжайте читать...) Обратите внимание, что если первый берет (скажем, ) пять секунд, чтобы завершить работу, а вторая потерпит неудачу за одну секунду, ваш код будет ждать полные пять секунд, прежде чем произойдет сбой.
К сожалению, в настоящее время нет синтаксиса await
для параллельного ожидания, поэтому у вас есть неловкость, которую вы перечислили, или Promise.all
. (Хотя обсуждалось await.all
или подобное, может быть, когда-нибудь.)
Версия Promise.all
:
const [value1, value2] = await Promise.all([getValue1Async(), getValue2Async()]);
... что более кратко, а также не ожидает завершения первой операции, если вторая не удалась быстро (например, в моем примере пять секунд/одна секунда выше, вышеприведенное будет отклонено в одну секунду, а не в ожидании 5). Также обратите внимание, что с вашим исходным кодом, если второе обещание отклоняется до разрешения первого обещания, вы вполне можете получить ошибку "необработанное отклонение" в консоли (в настоящее время вы используете Chrome v61; обновление: более новое версии имеют более интересное поведение), хотя эта ошибка, возможно, ложная (поскольку вы, в конечном счете, обрабатываете отклонение, поскольку этот код явно находится в функции async
), и поэтому функция будет перехватывать отклонение и сделать свое обещание отклонить вместе с ним) (обновление: еще раз, изменено). Но если оба обещания отклоняются, вы получите настоящую необработанную ошибку отклонения, потому что поток управления никогда не достигает const value2 = await p2;
и, таким образом, отклонение p2 никогда не обрабатывается.
Необработанные отклонения - это плохая вещь ™ (настолько, что скоро Node.js прервет процесс на действительно необработанных отклонениях, точно так же, как необработанные исключения &— потому что это то, что они есть), поэтому лучше избегать "получите обещание тогда await
это "шаблон в вашем вопросе.
Вот пример разницы во времени в случае сбоя (с использованием 500 мс и 100 мс, а не 5 секунд и 1 секунда) и, возможно, также, возможно, ложная необработанная ошибка отклонения (откройте настоящую консоль браузера, чтобы увидеть он):
const getValue1Async = () => {
return new Promise(resolve => {
setTimeout(resolve, 500, "value1");
});
};
const getValue2Async = () => {
return new Promise((resolve, reject) => {
setTimeout(reject, 100, "error");
});
};
// This waits the full 500ms before failing, because it waits
// on p1, then on p2
(async () => {
try {
console.time("separate");
const p1 = getValue1Async();
const p2 = getValue2Async();
const value1 = await p1;
const value2 = await p2;
} catch (e) {
console.error(e);
}
console.timeEnd("separate");
})();
// This fails after just 100ms, because it does not wait for p1
// to finish first, it rejects as soon as p2 rejects
setTimeout(async () => {
try {
console.time("Promise.all");
const [value1, value2] = await Promise.all([getValue1Async(), getValue2Async()]);
} catch (e) {
console.timeEnd("Promise.all", e);
}
}, 1000);
Open the real browser console to see the unhandled rejection error.