#node.js #async-await
#node.js #асинхронный-ожидание
Вопрос:
Я читал о «for await цикла», как описано в MDN. Это выглядит впечатляюще, поэтому я немного поиграл с некоторым кодом, но, к моему удивлению, я не могу перехватить ошибки, возникающие там.
'use strict';
const main = async() => {
const bar = [];
bar.push(new Promise((res) => { setTimeout(() => res(1), 1200); }));
bar.push(new Promise((res) => { setTimeout(() => res(2), 800); }));
bar.push(new Promise((res, rej) => { setTimeout(() => rej(3), 200); }));
try {
for await (const foo of bar) {
console.log('hey', foo);
}
} catch (err) {
console.error('err', err);
}
};
main();
Мой результат в основном такой, как я ожидаю. Но я не могу понять, почему я получаю необработанное promiserejection? Разве я даже не уловил эту ошибку?
gt; node await-loop.js
(node:10704) UnhandledPromiseRejectionWarning: 3
(node:10704) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:10704) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
hey 1
hey 2
err 3
(node:10704) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)
Может кто-нибудь, пожалуйста, помочь мне понять и написать правильную крылатую фразу здесь? Я что-то упускаю?
Ответ №1:
Необработанный отказ promiserejection?
Разве я даже не уловил эту ошибку?
Теоретически, да. (Ну, для этого и нужен блок catch, верно?)
Технически нет.
Main Stack
| try |
| |
| await bar[0] | <-- waiting to resolve till sometime after 1200
| |
| catch |
| |
После (приблизительно) 200 миллис:
bar [2] отклоняется и помещается в очередь, но await не выдал, потому что он все еще ожидал выхода bar [0], что происходит только после 1200 миллисекунд
Итак, на момент отклонения строки [2] ни catch
один блок не был доступен для обработки rejected promise
. Посмотрите на приведенный выше стек и обратите внимание, что try..catch
и bar[2]
не находятся в одном и том же стеке на момент отклонения, ну, по крайней мере, для компилятора (или концептуально для javascript).
Но вы заметили: err 3
который блок catch улавливает, когда, наконец, ожидаются результаты bar[2]
Если вы измените порядок обещаний и поместите отклоненное обещание в качестве первого элемента в массив, вы увидите, что блок catch сработал хорошо.
Или ожидание по-другому:
// unhandled rejection
try {
await bar[0]
await bar[1]
await bar[2]
} catch (e) {
console.error('error', e)
}
// Catch successfull
try {
await bar[2]
await bar[0]
await bar[1]
} catch (e) {
console.error('error', e)
}
Обновить:
Лучшим подходом было бы использовать Promise.all
, чтобы он быстро отказывал.
try {
const datas = await Promise.all(bar)
} catch (e) {
console.error(e)
}
Комментарии:
1. Спасибо за ваш ответ! Ваше решение не является практически, поскольку мне нужно на самом деле знать , какая асинхронная функция выдает ошибку (или, в данном случае, отклоняет обещание). Вы не возражаете против решения перехвата этой ошибки без перестановки моих вызовов?
2. Еще раз благодарю вас за обновление вашего ответа. Но я думаю, вы не уловили моего подхода. На самом деле я пытаюсь поймать ошибку при использовании
for await of
цикла. Если это возможно, я бы использовалPromise.all
, конечно. Мой пример должен только продемонстрировать, что необходимо для воспроизведения моей ситуации. Я все еще хочу использоватьfor await of
цикл, перебирающий асинхронные функции и все еще способный улавливать ошибки, если они возникают.3. Как я уже сказал в решении, это было бы невозможно.
async/await
это просто синтаксический сахар. за сценой, которую он используетgenerators
. Во время ожидания обещания дляyield
какого-либо другого обещания в массиве мог произойти сбой, поэтому в функции генератора не было бы блока catch в области видимости для обработки отклонения
Ответ №2:
for await of
Цикл — это просто сокращенный синтаксис для синхронного for of
цикла с символом await
в теле цикла. Таким образом, вы можете достичь того, чего хотите, с помощью этого:
'use strict';
const main = async() => {
const bar = [];
bar.push(new Promise((res) => { setTimeout(() => res(1), 1200); }));
bar.push(new Promise((res) => { setTimeout(() => res(2), 800); }));
bar.push(new Promise((res, rej) => { setTimeout(() => rej(3), 200); }));
for (const foo of bar) {
try {
const result = await foo;
console.log('hey', result);
} catch (err) {
console.error('err', err);
}
}
};
main();
Кстати, если вы хотите знать, какое обещание вызвало ошибку, это Promise.allSettled
может быть ближе к тому, что вы хотите, и более чистый код:
'use strict';
const main = async() => {
const bar = [];
bar.push(new Promise((res) => { setTimeout(() => res(1), 1200); }));
bar.push(new Promise((res) => { setTimeout(() => res(2), 800); }));
bar.push(new Promise((res, rej) => { setTimeout(() => rej(3), 200); }));
const results = await Promise.allSettled(bar);
/* Outputs:
[
{ status: 'fulfilled', value: 1 },
{ status: 'fulfilled', value: 2 },
{ status: 'rejected', reason: 3 }
]
*/
console.log(results);
};
main();
Комментарии:
1. Спасибо за ваш ответ! Но решение проблемы — это просто обходной путь.