Этот пример (repl.it) (от этот ответ) мне кажется, что он следует всем правилам в отношении обещаний. Тем не менее, его запуск регистрирует исключение, касающееся необработанного отклонения обещания, с соответствующим сообщением консоли. (Это также происходит в FF, Chrome и Node v10.)
Блок try / catch явно присутствует и завершает отклоненное обещание, так что же происходит и как я могу это исправить?
async function example() {
const start = Date.now()
let i = 0
function res(n) {
const id = ++i
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
console.info(`res #${id} called after ${n} milliseconds`, Date.now() - start)
}, n)
})
}
function rej(n) {
const id = ++i
return new Promise((resolve, reject) => {
setTimeout(() => {
reject()
console.info(`rej #${id} called after ${n} milliseconds`, Date.now() - start)
}, n)
})
}
try {
const delay1 = res(3000)
const delay2 = res(2000)
const delay3 = rej(1000)
const data1 = await delay1
const data2 = await delay2
const data3 = await delay3
} catch (error) {
console.info(`await finished`, Date.now() - start)
}
}
example()
🤔 А знаете ли вы, что...
JavaScript позволяет создавать динамические и интерактивные веб-приложения.
Проблема в том, что в момент, когда вызов rej
отклоняется, интерпретатор еще не дошел до строки, в которой await
передает обещание, созданное rej
, поэтому отклоненное обещание - это просто отклоненное обещание, а не Обещание, что текущий поток - await
ing:
try {
const delay1 = res(3000)
const delay2 = res(2000)
const delay3 = rej(1000)
const data1 = await delay1
// The interpreter is paused on the line above when `rej` rejects
const data2 = await delay2
const data3 = await delay3
Таким образом, поведение такое же, как если бы отклоненное обещание было объявлено без обработчика catch
. (Ошибки, выдаваемые Promises, будут обнаружены функцией async
, только если они являются в момент, когда обещание отклоняет с await
- в противном случае это просто приведет к необработанному отклонению обещания.)
Я бы предложил либо объявить обещания в тот же момент, когда вы их await
:
const data1 = await res(3000)
(примечание: время вышеупомянутого метода не будет таким же, как у исходного кода)
или используйте await Promise.all
для всех обещаний, это означает, что Promise
, который интерпретатор в настоящее время использует, будет выбрасывать (и, таким образом, входить в блок await
), как только один из обещаний отклоняет:
const [data1, data2, data3] = await Promise.all([
res(3000),
res(2000),
rej(1000)
]);
async function example() {
const start = Date.now()
let i = 0
function res(n) {
const id = ++i
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
console.info(`res #${id} called after ${n} milliseconds`, Date.now() - start)
}, n)
})
}
function rej(n) {
const id = ++i
return new Promise((resolve, reject) => {
setTimeout(() => {
reject()
console.info(`rej #${id} called after ${n} milliseconds`, Date.now() - start)
}, n)
})
}
try {
const [data1, data2, data3] = await Promise.all([
res(3000),
res(2000),
rej(1000),
]);
} catch (error) {
console.info(`error caught: await finished`, Date.now() - start)
}
}
example()
Чтобы выполнить дополнительную работу во время выполнения трех обещаний и отловить ошибки из этих обещаний, а также из основного потока, передайте элемент четвертый в catch
, IIFE, который выполняет дополнительную работу, которую вы хотите выполнить:
async function example() {
const start = Date.now()
let i = 0
function res(n) {
const id = ++i
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
console.info(`res #${id} called after ${n} milliseconds`, Date.now() - start)
}, n)
})
}
function rej(n) {
const id = ++i
return new Promise((resolve, reject) => {
setTimeout(() => {
reject()
console.info(`rej #${id} called after ${n} milliseconds`, Date.now() - start)
}, n)
})
}
try {
const [data1, data2, data3] = await Promise.all([
res(3000),
res(2000),
rej(1000),
(() => {
console.info('doing work...');
})()
]);
} catch (error) {
console.info(`error caught: await finished`, Date.now() - start)
}
}
example()