JavaScript — это однопоточный язык программирования. Другими словами, интерпретатор JavaScript может выполнять код только по одной строке за раз.
Рассмотрим следующее:
const longFunc = () => {
let count = 0;
while (count < Infinity) {
count++
}
console.log(3);
}
console.log(1);
console.log(2);
longFunc();
console.log(4);
Из-за синхронности этого кода, «4» не будет регистрироваться до тех пор, пока не завершится выполнение longFunc. Если longFunc никогда не завершится, console.log(4) никогда не будет достигнут!
Это называется блокировкой — выполнение longFunc блокирует выполнение дальнейшего кода до его завершения.
Возможно, вы уже заметили проблему, которая возникнет из-за такого поведения — как пользователь может наслаждаться приложением, которое не поддерживает многозадачность?
К счастью, есть несколько способов использовать асинхронныйкод, чтобы обойти проблемы, связанные с блокировкой.
Асинхронность в JavaScript означает не собственнуюмногопоточную обработку, а скорее использование цикла событий, очереди обратных вызовов и внешней обработки. API-интерфейсы, позволяющие более длительно работающей функции обрабатывать вне обычного потока кода.
Давайте проведем рефакторинг longFunc для использования setTimeout, асинхронной функции, с которой вы, возможно, уже знакомы.
const longFunc = () => {
let count = 0;
while (count < Infinity) {
count++
}
console.log(3);
}
console.log(1);
console.log(2);
setTimeout(longFunc, 0);
console.log(4);
console.log(5);
// 1
// 2
// 4
// 5
// ........
Хотя эта программа по-прежнему будет зависать после вызова longFunc, она успешно демонстрирует, как может работать асинхронный код.
Так что же здесь происходит?
Вызов setTimeout помещает ссылку на longFunc в очередь обратного вызова. Также известная как очередь сообщений или очередь событий, очередь обратного вызовасодержит ожидающий код, который будет выполняться только после того, как стек вызовов пусто.
Как только каждая функция в стеке вызовов вернется, цикл обработки событий определит, прошло ли указанное время. Если да, то функция longFunc будет извлечена из очереди и вызовет Это.
Вот в чем загвоздка: setTimeout не является родным для JavaScript. Поскольку JavaScript является синхронным, все асинхронные функции должны поступать из внешнего API — в случае setTimeout, обобщенного веб-API, предоставляемого веб-браузерами.
Имея это в виду, становится легче понять, как работают такие функции, как fs в Node: при выполнении вызовов fs.readFile() или fs.writeFile(), операция передается внутренностям* Node для выполнения работы, в то время как остальная часть кода страницы работает.
* Я не знаком с фактическим процессом — пожалуйста, оставьте комментарий, если знаете.
Если это имеет смысл, вы готовы перейти к шаблону обратного вызова с ошибкой сначала шаблону обратного вызова и чудесам Promises.
Если нет, оставьте комментарий, и я дам вам руку.