что такое асинхронный код блока

Используй Async/Await в JavaScript, как профессионал

что такое асинхронный код блока

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

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

Что такое «асинхронный код»?

Асинхронность имеет место при наличии как минимум двух событий, происходящих в разное время. Рассмотрим пример:

Здесь код выполняется в том порядке, какой и ожидается. Такой код называется синхронным.

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

Что произошло с выводом?

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

А произошло вот что: функция времени ожидания timeout была вызвана одновременно с выполнением остальной части кода, но запуск внутреннего log произошел спустя 500 мс после этого. Отсюда и выдача неверного вывода.

Строка кода выполнилась в другое время, нежели остальные. Это и называется асинхронностью.

И как же решить эту проблему?

Промисы

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

Примечание: в этой статье не будем слишком подробно разбирать промисы, нам важно иметь общее представление о них.

Рассмотрим следующий пример. Вывод снова в норме:

Но как это получилось?

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

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

Но есть ли более лучшее решение?

Вводим async/await

Используя ключевые слова async/await в JavaScript, мы делаем код гораздо более отточенным и удобным для восприятия. Посмотрим, что произойдет с кодом при задействовании async/await в предыдущем примере:

Теперь код выглядит намного лучше!

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

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

Предположим, теперь нам надо добавить в пример еще одну часть асинхронного кода. Что будет в этом случае?

Обратите внимание: здесь мы добавляем еще один кусок асинхронного кода и вывод остается в том же порядке, какой ожидался.

Посмотрите: для второй асинхронной функции выполнение задано на 30 мс раньше первой. Так почему же выполнение не произошло в таком порядке?

В синхронной ситуации вторая функция действительно была бы вызвана раньше первой и произошла бы выдача неверного вывода.

Рассмотрим более практический пример с использованием async/await :

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

Дальше отображаем список imposters и показываем, где они находятся в данных о персонаже.

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

Заключение

Ну вот и все. Понять, как работать с асинхронным кодом, бывает непросто, особенно когда имеешь с ним дело впервые. Чем больше сталкиваешься с асинхронным кодом, тем лучше с такими ситуациями справляешься.

Надеюсь, вы получили некоторое представление о том, как работает async/await и очень скоро станете еще большим профессионалом в JavaScript.

Источник

Как я понимаю асинхронный код?

Привет, Хабр! Представляю вашему вниманию перевод (с небольшими корректировками) статьи «How Do I Think About Async Code?!» автора Leslie Richardson.

Асинхронный код становится все более популярным для написания отзывчивых приложений. К сожалению, асинхронное программирование так же привносит дополнительные трудности. Как следствие, понять, как работает такой код, может быть непростой задачей, вне зависимости от вашего опыта. Если вы только начали работать с асинхронным кодом, или вы захотели освежить свое понимание – это введение в мир асинхронного программирования!

Что такое асинхронный код?

Асинхронное программирование позволяет вам выполнить блок кода без остановки (или блокировки) всего потока, в котором выполняется действие. Распространенный миф об асинхронном коде заключается в том, что он улучшает производительность, что не всегда верно. Вместо этого главная особенность асинхронного программирования заключается в том, что оно увеличивает количество задач (пропускную способность), которые могут выполняться одновременно, без необходимости блокировать поток, в котором эти действия выполняются.

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

Почему мне стоит использовать асинхронный код? Пример, пожалуйста!

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

Синхронный метод MakeCake()

что такое асинхронный код блока

Синхронная программа выпекания пирога

что такое асинхронный код блока

В реальной жизни вы, как правило, разделяете этот процесс на задачи, замешиваете тесто, пока духовка разогревается. Или делаете глазурь, в то время как пирог запекается в духовке. Это увеличивает вашу производительность и позволяет испечь торт намного быстрее. Это как раз тот случай, где асинхронный код пригодится! Сделав наш текущий код асинхронным, мы сможем заняться другими делами, чтобы скоротать время, в то время пока мы ожидаем(await) результата задачи(task), такой как выпекание пирога в духовке.
Чтобы сделать это – изменим наш код, а так же добавим метод PassTheTime. Теперь наш код сохраняет состояние задачи, запускает другую синхронную или асинхронную операцию и получает результат сохраненной задачи, в тот момент, когда это необходимо.

Асинхронный метод MakeCake()
что такое асинхронный код блока

Асинхронная программа выпечки пирога
что такое асинхронный код блока

По сравнению с синхронным методом MakeCake, в котором отсутствует метод PassTheTime, асинхронному методу MakeCakeAsync удается выполнить больше задач, не блокируя поток, что сокращает время, необходимое для выполнения всего метода в целом.

Сравнение асинхронной и синхронной программ

что такое асинхронный код блока

C # позволяет писать асинхронный код, используя тип Task и ключевые слова await и async. Тип Task сообщает вызывающей стороне о возможном типе возвращаемого значения. Он также указывает на то, что другие действия могут продолжать выполняться в вызвавшем его методе. Ключевое слово async работает в паре с ключевым словом await, которое уведомляет компилятор о том, что нам потребуется возвращаемое методом значение, но не сразу. В результате нам не нужно блокировать вызывающий поток, и мы можем продолжать выполнение других задач, пока не потребуется ожидаемое значение. Первоначально асинхронный метод будет выполняться синхронно, пока не будет найдено ключевое слово await. Это именно тот момент, когда выполнение метода начнется асинхронно.

Я узнал об асинхронном коде! Что теперь?

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

Приложения, использующие HTTP-запросы — в зависимости от запроса, обработка HTTP-вызовов может занять длительное время. Использование асинхронного кода позволяет нам выполнить другие операции, пока мы ожидаем ответа от сервера.

Пример запроса HTTP GET

что такое асинхронный код блока

Приложения, с пользовательским интерфейсом — приложения WPF или любые другие, использующие кнопки, текстовые поля и другие ресурсы UX, также отлично подходят для асинхронной реализации. Например, приложение WPF, производящее анализ файла. Данная процедура может занять некоторое время. Однако, сделав это действие асинхронным, вы по-прежнему сможете взаимодействовать с пользовательским интерфейсом, не останавливая приложение полностью, во время ожидания завершения операции.

Источник

JavaScript: методы асинхронного программирования

Синхронный код на JavaScript, автор которого не стремился сбить с толку тех, кто этот код будет читать, обычно выглядит просто и понятно. Команды, из которых он состоит, выполняются в том порядке, в котором они следуют в тексте программы. Немного путаницы может внести поднятие объявлений переменных и функций, но чтобы превратить эту особенность JS в проблему, надо очень постараться. У синхронного кода на JavaScript есть лишь один серьёзный недостаток: на нём одном далеко не уехать.

что такое асинхронный код блока

Практически каждая полезная JS-программа написана с привлечением асинхронных методов разработки. Здесь в дело вступают функции обратного вызова, в просторечии — «коллбэки». Здесь в ходу «обещания», или Promise-объекты, называемые обычно промисами. Тут можно столкнуться с генераторами и с конструкциями async/await. Асинхронный код, в сравнении с синхронным, обычно сложнее писать, читать и поддерживать. Иногда он превращается в совершенно жуткие структуры вроде ада коллбэков. Однако, без него не обойтись.

Сегодня предлагаем поговорить об особенностях коллбэков, промисов, генераторов и конструкций async/await, и подумать о том, как писать простой, понятный и эффективный асинхронный код.

О синхронном и асинхронном коде

Начнём с рассмотрения фрагментов синхронного и асинхронного JS-кода. Вот, например, обычный синхронный код:

Он, без особых сложностей, выводит в консоль числа от 1 до 3.

Теперь — код асинхронный:

Возможно, если вы только начинаете путь JS-разработчика, вы зададитесь вопросами: «Зачем это всё? Может быть, можно переделать асинхронный код в синхронный?». Поищем ответы на эти вопросы.

Постановка задачи

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

В плане интерфейса ограничимся чем-нибудь простым.

что такое асинхронный код блока
Простой интерфейс поиска пользователей GitHub и соответствующих им репозиториев

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

Функции обратного вызова

С функциями в JS можно делать очень много всего, в том числе — передавать в качестве аргументов другим функциям. Обычно так делают для того, чтобы вызвать переданную функцию после завершения какого-то процесса, который может занять некоторое время. Речь идёт о функциях обратного вызова. Вот простой пример:

Используя этот подход для решения нашей задачи, мы можем написать такую функцию request :

Разберём то, что здесь происходит:

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

что такое асинхронный код блока
Ад коллбэков во всей красе. Изображение взято отсюда.

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

Промисы

Используя промисы можно улучшить читабельность кода. В результате, например, если в ваш проект придёт новый разработчик, он быстро поймёт, как там всё устроено.

Для того, чтобы создать промис, можно воспользоваться такой конструкцией:

Разберём этот пример:

Для того, чтобы не погрязнуть в теории, вернёмся к нашему примеру. Перепишем его с использованием промисов.

что такое асинхронный код блока
Это — промис в состоянии ожидания. Он может быть либо успешно разрешён, либо отклонён

Благодаря такому подходу мы разобрались с состоянием гонки и с некоторыми возникающими при этом проблемами. Ада коллбэков тут не наблюдается, но код пока ещё читать не так-то легко. На самом деле, наш пример поддаётся дальнейшему улучшению за счёт выделения из него объявлений функций обратного вызова:

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

Генераторы

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

Посмотрим теперь, как это всё применить к нашей задаче. Итак, вот функция request :

Генератор будет выглядеть так:

Вот что здесь происходит:

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

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

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

Async/await

Здесь происходит следующее:

Этот подход и использование промисов — мои любимые методы асинхронного программирования. Код, написанный с их использованием, удобно и читать и править. Подробности об async/await можно почитать здесь.

Итоги

В зависимости от особенностей поставленной перед вами задачи, может оказаться так, что вы будете пользоваться async/await, коллбэками, или некоей смесью из разных технологий. На самом деле, ответ на вопрос о том, какую именно методику асинхронной разработки выбрать, зависит от особенностей проекта. Если некий подход позволяет решить задачу с помощью читабельного кода, который легко поддерживать, который понятен (и будет понятен через некоторое время) вам и другим членам команды, значит этот подход — то, что вам нужно.

Уважаемые читатели! Какими методиками написания асинхронного кода на JavaScript вы пользуетесь?

Источник

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

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

Необходимые знания:Базовая компьютерная грамотность, знакомство с основами JavaScript.
Цель:Понять основные идеи асинхронного программирования, и как они проявляются в веб-браузерах и JavaScript.

Что же такое Асинхронность?

Как правило, программный код выполняется последовательно, только одна конкретная операция происходит в данный момент времени. Если функция зависит от результата выполнения другой функции, то она должна дождаться пока нужная ей функция не завершит свою работу и не вернёт результат и до тех пор пока это не произойдёт, выполнение программы, по сути, будет остановлено с точки зрения пользователя.

что такое асинхронный код блока

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

Блокировка кода

Асинхронные техники очень полезны, особенно при веб разработке. Когда ваше приложение запущено в браузере и выполняет свои задачи, не возвращая контроль окружению, браузер может подвисать. Это называется блокировка; браузер заблокирован и не может реагировать на действия пользователя и выполнять служебные.задачи, до тех пор пока веб приложение не освободит ресурсы процессора.

Давайте рассмотрим несколько примеров, которые покажут, что именно значит блокировка.

В нашем simple-sync.html примере (see it running live), добавим кнопке событие на клик, чтобы при нажатии на неё запускалась трудоёмкая операция (расчёт 10000000 дат, и вывод последней рассчитанной даты на консоль) после чего в DOM добавляется ещё один параграф:

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

Примечание: Предыдущий пример слишком не реальный. Вам никогда не понадобится считать столько дат в реальном приложении! Однако, он помогает вам понять основную идею.

В нашем следующем примере, simple-sync-ui-blocking.html (посмотреть пример), мы сделаем что-нибудь более реалистичное, с чем вы сможете столкнуться на реальной странице. Мы заблокируем действия пользователя отрисовкой страницы. В этом примере у нас две кнопки:

Если вы быстро нажмёте на первую кнопку и затем быстро кликните на вторую, вы увидите, что предупреждение не появится на странице, пока все круги не будут отрисованы. Первая операция блокирует выполнение следующей до тех пор пока не завершится сама.

Примечание: Хорошо, в приведённом некрасивом примере, мы получили эффект блокировки, который показывает общую проблему при разработке приложений, с которой все время приходится бороться разработчикам.

Почему так происходит? Потому что JavaScript, в общем случае, выполняет команды в одном потоке. Пришло время познакомиться с понятием потока.

Потоки

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

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

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

JavaScript однопоточный

JavaScript, традиционно для скриптовых языков, однопоточный. Даже, если есть несколько ядер, вы можете использовать их только для выполнения задач в одном потоке, называемом основной поток. Наш пример выше, выполняется следующим образом:

В итоге, JavaScript получил несколько инструментов, которые могут помочь в решении подобных проблем. Web workers позволяют вам обработать некоторый JavaScript-код в отдельном потоке, который называется обработчик, таким образом вы можете запускать отдельные блоки JavaScript-кода одновременно. В основном, вы будете использовать воркеры, чтобы запустить ресурсоёмкий процесс, отдельно от основного потока, чтобы не блокировать действия пользователя.

Помня об этом, выполните наш следующий пример simple-sync-worker.html (посмотреть пример в действии), с открытой консолью. Это переписанный предыдущий пример, который теперь рассчитывает 10 миллионов дат в отдельном потоке обработчика. Теперь, когда вы нажимаете на кнопку, браузер может добавить новый элемент на страницу, до того как все даты будут посчитаны. Самая первая операция больше не блокирует выполнение следующей.

Асинхронный код

Воркеры полезный инструмент, но у них есть свои ограничения. Самое существенное, заключается в том, что они не имеют доступа к DOM — вы не можете использовать воркер для обновления UI. Мы не можем отрисовать миллион наших точек внутри воркера; он может только обработать большой объем информации.

Следующая проблема заключается в том, что даже если код запущенный в воркере ничего не блокирует, он в целом остаётся синхронным. Это проблема появляется, когда какой-то функции требуются результаты выполнения нескольких предыдущих функций. Рассмотрим следующую диаграмму потоков:

В этом примере, предположим Task A делает что-то вроде получения картинки с сервера а Task B затем делает что-нибудь с полученной картинкой, например, применяет к ней фильтр. Если запустить выполняться Task A и тут же попытаться выполнить Task B, то вы получите ошибку, поскольку картинка ещё не будет доступна.

Теперь, давайте предположим, что Task D использует результат выполнения обеих задач Task B и Task C. Если мы уверенны, что оба результата будут доступны одновременно, тогда не возникнет проблем, однако, часто это не так. Если Task D попытаться запустить, когда какого-то нужного ей результата ещё нет, выполнение закончится ошибкой.

Чтобы избежать подобных проблем, браузеры позволяют нам выполнять определённые операции асинхронно. Такие возможности, как Promises позволяют запустить некоторую операцию (например, получение картинки с сервера), и затем подождать пока операция не вернёт результат, перед тем как начать выполнение другой задачи:

Поскольку операция выполняется где-то отдельно, основной поток не блокируется, при выполнении асинхронных задач.

В следующей статье, мы покажем вам, как писать асинхронный код. Захватывает дух, неправда ли? Продолжайте читать!

Заключение

При проектировании современных программ все больше используется асинхронное программирование, чтобы программа имела возможность выполнять несколько операций в конкретный момент времени. Как только вы начнёте использовать новые, более мощные возможности API, вы обнаружите множество ситуаций, где решить нужную задачу можно только асинхронно. Раньше было сложно писать асинхронный код. До сих пор, нужно время, чтобы привыкнуть к такому подходу, но процесс стал намного легче. Далее, в этом разделе, мы будем глубже исследовать вопрос, когда же асинхронный код необходим и как спроектировать программу, чтобы избежать проблем, описанных выше.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *