У меня есть очень простой фрагмент кода, подобный этому
async function neverResolve() {
return new Promise(() => {
console.info("This promise will never resolve");
});
}
(async () => {
try {
console.info("START");
// neverResolve().then().catch(); // uncommenting this line works as expected
await neverResolve();
await new Promise((resolve) => setTimeout(() => resolve(), 5000));
console.info("END");
} catch (error) {
console.info("ERR: ", error);
}
})();Почему вышеуказанная функция не ждет 5 секунд и не печатает END.
Он автоматически завершается после печати
START
This promise will never resolve
Но если мы выполним ту же функцию, но с конструкцией .then(), я получу ожидаемый результат.
async function neverResolve() {
return new Promise(() => {
console.info("This promise will never resolve");
});
}
(async () => {
try {
console.info("START");
neverResolve().then().catch();
await new Promise((resolve) => setTimeout(() => resolve(), 5000));
console.info("END");
} catch (error) {
console.info("ERR: ", error);
}
})();neverResolve().then().catch(); не ждет. Это огонь и забыть. Поэтому код будет продолжаться после него. Первый никогда не разрешается, и в конечном итоге процесс уничтожается. Нет реальной причины хранить его вечно.
@GuillaumeBrunerie не имеет большого значения. С async function обещание от neverResolves() примет состояние возвращенного обещания. Итак, это просто то же неразрешимое обещание в конце.
Я добавляю несколько общих советов: async/await — это просто синтаксический сахар. Когда вы испытываете другое поведение в коде, который является просто синтаксическим сахаром, вы можете просто вставить его в BabelJS и посмотреть, как он преобразуется.
await new Promise(() => {}); это зло



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


В первом примере await neverResolve(); ждет вечно и никогда не разрешается, как указано. Javascript может отключаться и делать другие вещи в других задачах, ожидая этого.
Во втором примере, добавив .then(), вы сказали javascript продолжить обработку кода ниже. Как правило, весь приведенный ниже код будет либо внутри обратного вызова для then(), либо для catch(), что затем создаст ту же паузу, которую вы видите.
Для этих нюансов есть очень преднамеренные причины, которые позволяют вам отправить запрос на выборку, выполнить другую работу, а затем вернуться, чтобы посмотреть, сделана ли выборка позже. См. мои комментарии в этом размеченном и слегка измененном примере.
async function slowFetch() {
return new Promise((resolve) => {
// a fetch statement that takes forever
fetch().then(data => resolve(data));
});
}
(async () => {
try {
console.info("START");
// start the slow fetch right away
const dataPromise = slowFetch();
// do some other tasks that don't take as long
await new Promise((resolve) => setTimeout(() => resolve(), 5000));
// wait for the data to arrive
const data = await dataPromise;
// do something with the data like fill in the page.
console.info("END");
} catch (error) {
console.info("ERR: ", error);
}
})();
ОП спрашивает, почему возврат промиса, который никогда не вызывает resolve(), не выполняет блок кода после его ожидания, но в вашем примере вы выполняете resolve() inside a slowFetch()``
new Promise((resolve) => { fetch().then(data => resolve(data));}); — исключительно плохой пример, так как он просто поощряет бессмысленный и опасный явный антипаттерн построения промисов. Тот, что в этом коде, никогда не обрабатывает отклоненное обещание fetch(), поэтому существует реальный риск стать неразрешимым.
Да, я отвечаю на это в своем первом предложении, а затем сравниваю его с тем, как работают и продолжают обрабатываться вторые примеры. Наконец, я попытался собрать воедино пример того, почему вы потенциально не сразу ожидаете обещания, которое охватывает как оба примера, так и исходный вопрос.
Делая neverResolve().then().catch(); то, что вы на самом деле сделали;
async function neverResolve() {
return new Promise(() => {
console.info("This promise will never resolve");
});
}
(async () => {
try {
console.info("START");
(async function(){
try {
await neverResolve()
}
catch{
}
})();
await new Promise((resolve) => setTimeout(() => resolve(), 5000));
console.info("END");
} catch (error) {
console.info("ERR: ", error);
}
})();Внутренний асинхронный IIFE работает так же, как neverResolve().then().catch();. Вы не должны смешивать обещания с async await абстракцией.
Почему вышеуказанная функция не ждет 5 секунд и печатает END. Он автоматически завершается после печати
Потому что ваш код никогда не проходит мимо этого:
await neverResolve();
Поскольку neverResolve() возвращает промис, который никогда не разрешается и не отклоняется, эта функция навсегда приостанавливается на этой строке, а строки кода после этого оператора в функции никогда не выполняются.
await означает, что выполнение функции должно быть приостановлено на неопределенный срок, пока обещание, которое вы ожидаете, не будет разрешено или отклонено.
Но если мы выполним ту же функцию, но с конструкцией .then(), я получу ожидаемый результат.
Когда вы меняете await на это:
neverResolve().then().catch();
Выполнение функции вообще НЕ приостановлено. Выполняется neverResolve(). Это возвращает обещание, которое затем вызывает .then(). Этот вызов .then() просто регистрирует обратный вызов с обещанием (который будет вызываться позже, когда обещание разрешается). Это возвращает другое обещание, которое затем вызывает .catch(), на котором просто регистрируется обратный вызов с обещанием (который будет вызываться позже, если/когда обещание отклоняется).
Теперь вы даже не передаете обратный вызов в любом случае, поэтому эти .then() и .catch() на самом деле нечего делать, но даже если вы передадите обратный вызов каждому из них, они просто зарегистрируют этот обратный вызов и немедленно вернутся. .then() и .catch() не блокируются. Они просто регистрируют обратный вызов и тут же возвращаются. Итак, после того, как они вернутся, будут выполнены следующие строки кода в функции, и вы получите ожидаемый результат.
Резюме
await приостанавливает выполнение функции до тех пор, пока обещание, которое вы ожидаете, не будет разрешено или отклонено.
.then() и .catch() просто зарегистрируйте обратные вызовы для некоторого будущего изменения состояния обещания. Они не блокируют. Они не приостанавливают выполнение функции. Они регистрируют обратный вызов и тут же возвращаются.
neverResolve не разрешает и не отклоняет, поэтому программа зависает на неопределенное время на await. Рассмотрите возможность абстрагирования функциональности тайм-аута в своей собственной универсальной функции, timeout -
const sleep = ms =>
new Promise(r => setTimeout(r, ms));
const timeout = (p, ms) =>
Promise.race([
p,
sleep(ms).then(() => { throw Error("timeout") })
]);
const neverResolve = () => new Promise(() => {});
(async function() {
try {
console.info("connecting...");
await timeout(neverResolve(), 2000);
}
catch (err) {
console.error(err);
}
})();Вот что происходит в вашем случае:
Ваша программа начинает выполнение с анонимной асинхронной функции IIFE, поскольку это асинхронная функция, она немедленно возвращает обещание в глобальную область. Таким образом, выполнение анонимного IIFE откладывается.
Вы можете легко проверить это, добавив console.info("OK"); в конце вызова IIFE, который выводится на консоль.
Node ведет учет таких вещей, как таймеры и сетевые запросы. Когда вы делаете сетевой или другой асинхронный запрос, устанавливаете таймер и т. д. Node добавляет к этому счетчику ссылок. Когда время/запрос разрешено, Node вычитает из подсчета. Реф. ссылка на видео Итак, что происходит внутри вашего IIFE:
console.info("START"); <--- выводится на консольawait neverResolve(); Здесь все становится интереснее, этот вызов await откладывает выполнение и блокируется до тех пор, пока не будет выполнен обратный вызов, либо resolve, либо reject.Но в этом случае обратные вызовы не зарегистрированы, и nodejs будет думать, что закончил обработку всего запроса, и завершит процесс.
Это имеет смысл после просмотра ссылки на видео, которую вы разместили, и объяснение было по делу.
Попробуйте удалить ключевое слово
asyncперед определениемneverResolves. В противном случае вы заключаете его в два обещания, а это не то, что вы хотели сделать.