TL: DR
Обещания и функции async
не переносят ваш код в другой поток.Если вы хотите переместить этот длительный процесс из основного потока, в браузерах посмотрите на веб-работников , а в Node.js посмотрите на дочерних процессов .
Подробности
Обещания и функции async
(которые являются всего лишь синтаксисом для создания и использования обещаний) не переносят вашу обработку в какой-либо другой поток, это все равно происходит в том же потоке, в котором вы запускаете процесс.Единственное, что они делают , это обеспечивают асинхронный вызов обратных вызовов then
и catch
.Они не делают ваш код асинхронным (кроме того, что обратные вызовы происходят асинхронно).
Таким образом, ваш первый блок, использующий setTimeout
, просто устанавливает тайм-аут, возвращает обещание, а затем, когда тайм-аутистекает, он блокирует основной поток, пока выполняется медленно работающий процесс.Это просто меняется, когда блокировка происходит немного, это не меняет факт блокировки.
Вы можете увидеть этот эффект здесь, обратите внимание, как счетчик останавливается, когда происходит длительный процесс:
function slowFunction() {
return new Promise(resolve => {
setTimeout(() => {
const stop = Date.now() + 2000;
while (Date.now() < stop) {
// busy wait (obviously, never really do this)
}
}, 1000);
});
};
console.log("before slowFunction");
slowFunction()
.then(() => {
console.log("then handler on slowFunction's promise");
})
.catch(console.error);
console.log("after slowFunction");
let counter = 0;
const timer = setInterval(() => {
console.log(++counter);
}, 100);
setTimeout(() => {
clearInterval(timer);
console.log("done");
}, 3000);
.as-console-wrapper {
max-height: 100% !important;
}
Ваш второй блок, не использующий setTimeout
, просто блокирует сразу, потому что функция исполнителя обещаний (функция, которую вы передаете new Promise
) запускается немедленно и синхронно,и вы ничего не делаете, чтобы сделать это асинхронным.
Вы можете увидеть это здесь;счетчик делает паузу сразу, не позже:
function slowFunction() {
return new Promise(resolve => {
const stop = Date.now() + 2000;
while (Date.now() < stop) {
// busy wait (obviously, never really do this)
}
});
};
console.log("before slowFunction");
slowFunction()
.then(() => {
console.log("then handler on slowFunction's promise");
})
.catch(console.error);
console.log("after slowFunction");
let counter = 0;
const timer = setInterval(() => {
console.log(++counter);
}, 100);
setTimeout(() => {
clearInterval(timer);
console.log("done");
}, 3000);
.as-console-wrapper {
max-height: 100% !important;
}
Мы даже не видим журнал до slowFunction до тех пор, пока не завершится долгосрочный код, потому что браузер так и не получилшанс перекрасить, у нас была нить захвачена.
Относительно async
функций: код в async
функции начинается синхронно и является синхронным до первого await
(или другая конструкция, такая как setTimeout
, которая планирует выполнение вещей позже).Только код после этого является асинхронным (потому что он должен был ждать).
Вот пример, демонстрирующий, что:
async function foo() {
console.log("before await");
await Promise.resolve();
console.log("after await");
}
console.log("before foo");
foo()
.then(() => {
console.log("then handler on foo's promise");
})
.catch(console.error);
console.log("after foo");
Вот результат этого:
before foo
before await
after foo
after await
then handler on foo's promise
Обратите внимание, как до ожидания происходит до после foo ;это синхронно со звонком на foo
.Но тогда после ожидания не происходит позже (потому что await Promise.resolve()
должен заставить код, следующий за ним, выполняться асинхронно; это синтаксический сахар для then
, который обещает не вызывать свой обработчик синхронно, даже еслиобещание уже выполнено).