Новости

06.10.2021

Книга «JavaScript для профессиональных веб-разработчиков. 4-е международное изд.»

Автор сразу переходит к техническим деталям, которые сделают ваш код чистым и переведут вас с уровня рядового кодера на высоту продвинутого разработчика.

Рост мобильного трафика увеличивает потребность в адаптивном динамическом веб-дизайне, а изменения в JS-движках происходят постоянно, так что каждый веб-разработчик должен постоянно обновлять свои навыки работы с JavaScript.

В книге вы найдете:

  • Последнюю информацию о классах, промисах, async/await, прокси, итераторах, генераторах, символах, модулях и операторах spread/rest.
  • Фундаментальные концепции веб-разработки, такие как DOM, BOM, события, формы, JSON, обработка ошибок и веб-анимация.
  • Расширенные API-интерфейсы, такие как геолокация, service workers, fetch, атомизация, потоки, каналы сообщений и веб-криптография.
  • Сотни рабочих примеров кода, которые ясно и кратко иллюстрируют концепции.

Введение в асинхронное программирование


Двойственность между синхронным и асинхронным поведением является фундаментальной концепцией в computer science, особенно в однопоточной модели цикла событий, такой как JavaScript. Асинхронное поведение обусловлено необходимостью оптимизации для более высокой вычислительной пропускной способности в условиях операций с высокой задержкой. Это прагматично, если возможно выполнение других инструкций во время завершения вычислений и при этом поддерживается стабильное состояние системы.

Важно отметить, что асинхронная операция необязательно является вычислительной операцией или операцией с высокой задержкой. Ее можно использовать везде, где нет смысла блокировать поток выполнения, чтобы дождаться возникновения асинхронного поведения.

Синхронный и асинхронный JavaScript


Синхронное поведение аналогично последовательным инструкциям процессора в памяти. Каждая инструкция выполняется строго в том порядке, в котором она появляется, и каждая из них также способна немедленно извлекать информацию, которая хранится локально в системе (например, в регистре процессора или в системной памяти). В результате можно легко определить состояние программы (например, значение переменной) в любой заданной точке кода.

Тривиальным примером этого будет выполнение простой арифметической операции:

На каждом шаге этой программы можно рассуждать о ее состоянии, потому что выполнение не будет продолжено, пока не будет выполнена предыдущая инструкция. Когда последняя инструкция завершается, вычисленное значение x сразу становится доступным для использования.

Этот фрагмент JS-кода легко разложить по полочкам, потому что нетрудно предвидеть, к каким низкоуровневым инструкциям он будет компилироваться (например, от JavaScript до x86). Предположительно, операционная система выделит некоторое количество памяти для числа с плавающей точкой в стеке, выполнит арифметическую операцию с этим значением и запишет результат в выделенную память. Все эти инструкции располагаются последовательно внутри одного потока выполнения. В каждой точке скомпилированной низкоуровневой программы можно с большой долей уверенности утверждать, что можно и что нельзя знать о состоянии системы.

И наоборот, асинхронное поведение аналогично прерываниям, когда объект, внешний по отношению к текущему процессу, может инициировать выполнение кода. Часто требуется асинхронная операция, потому что невозможно заставить процесс долго ждать завершения операции (как в случае синхронной операции). Это длительное ожидание может возникнуть из-за того, что код обращается к ресурсу с высокой задержкой, например, отправляет запрос на удаленный сервер и ожидает ответа.

Тривиальным примером JavaScript в этом случае будет выполнение арифметической операции за время ожидания:

Эта программа в конечном итоге выполняет ту же работу, что и синхронная — складывая два числа вместе, — но этот поток выполнения не может точно знать, когда изменится значение x, потому что это зависит от того, когда обратный вызов будет исключен из очереди сообщений и выполнен.

Этот код не так легко разложить по полочкам. Хотя низкоуровневые инструкции, используемые в этом примере, в конечном итоге выполняют ту же работу, что и предыдущий пример, второй блок инструкций (операция сложения и назначение) запускается системным таймером, который генерирует прерывание для постановки в очередь на выполнение. В тот момент, когда прерывание будет запущено, это станет черным ящиком для среды выполнения JavaScript, поэтому невозможно точно знать, когда именно произойдет прерывание (хотя оно гарантированно произойдет после завершения текущего потока синхронного выполнения, поскольку обратный вызов еще не был снят с выполнения и утилизирован). Тем не менее обычно нельзя утверждать, когда именно состояние системы изменится после запланирования обратного вызова.

Чтобы значение x стало полезным, эта асинхронно выполняемая функция должна сообщить остальной части программы, что она обновила значение x. Однако если программе не нужно это значение, тогда она может продолжить и выполнять другую работу вместо ожидания результата.

Разработать систему, которая будет знать, когда можно прочитать значение x, на удивление сложно. Реализации такой системы в JavaScript прошли несколько итераций.

Устаревшие паттерны асинхронного программирования


Асинхронное поведение долгое время было важным, но ужасным краеугольным камнем JavaScript. В ранних версиях языка асинхронная операция поддерживала только определение функции обратного вызова для указания, что асинхронная операция завершена. Сериализация асинхронного поведения была распространенной проблемой, обычно решаемой с помощью кодовой базы, полной вложенных функций обратного вызова, в миру называемой «адом обратных вызовов».

Предположим, вы работали со следующей асинхронной функцией, которая использует setTimeout для выполнения некоторого поведения через одну секунду:

Здесь не происходит ничего таинственного, но важно точно понять, почему эта функция асинхронна. setTimeout позволяет определить обратный вызов, который планируется выполнить по истечении заданного промежутка времени. Спустя 1000 мс во время выполнения JavaScript запланирует обратный вызов, поместив его в очередь сообщений JavaScript. Этот обратный вызов снимается и выполняется способом, который полностью невидим для кода JavaScript. Более того, функция double() завершается сразу после успешного выполнения операции планирования setTimeout.

Возврат асинхронных значений

Предположим, операция setTimeout вернула полезное значение. Как лучше всего вернуть значение туда, где оно необходимо? Широко используемая стратегия заключается в предоставлении обратного вызова для асинхронной операции, где обратный вызов содержит код, требующий доступ к вычисленному значению (предоставляется в качестве параметра). Это выглядит следующим образом:

Здесь при вызове setTimeout команда помещает функцию в очередь сообщений по истечении 1000 мс. Эта функция будет удалена и асинхронно вычислена средой выполнения. Функция обратного вызова и ее параметры все еще доступны в асинхронном исполнении через замыкание функции.

Обработка ошибок

Вероятность сбоя также должна быть включена в эту модель обратного вызова, так что обычно она принимает форму обратного вызова в случае успеха и неудачи:

Этот формат уже нежелателен, так как обратные вызовы должны быть определены при инициализации асинхронной операции. Значение, возвращаемое из асинхронной функции, является временным, и поэтому только обратные вызовы, которые готовы принять это временное значение в качестве параметра, могут получить к нему доступ.

Вложенные асинхронные обратные вызовы

Ситуация с обратными вызовами еще более усложняется, когда доступ к асинхронным значениям зависит от других асинхронных значений. В мире обратных вызовов это требует вложения обратных вызовов:

Неудивительно, что эта стратегия обратного вызова плохо масштабируется по мере роста сложности кода. Выражение «ад обратных вызовов» вполне заслужено, так как кодовые базы JavaScript, которые были поражены такой структурой, стали почти не поддерживаемыми.

С полным содержанием статьи можно ознакомиться на сайте "Хабрахабр":

https://habr.com/ru/company/piter/blog/577812/


Комментарии: 0

Пока нет комментариев


Оставить комментарий






CAPTCHAОбновить изображение

Наберите текст, изображённый на картинке

Все поля обязательны к заполнению.

Перед публикацией комментарии проходят модерацию.