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

Синхронный и асинхронный код в JavaScript

Рассмотрим следующий код:

Рассмотрим теперь следующий код:

Событийная модель асинхронна

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

Загрузка картинок асинхронна

Рассмотрим следующий код:

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

Где еще будет асинхронность?

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

Наши заглушки-имитаторы

Это может быть все, что угодно: таймер, загрузка картинки, AJAX запрос к серверу, чтение или запись файла через NodeJS и так далее.

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

Давайте теперь воспользуемся нашей функцией в коде:

Основная задача в асинхронности

Посмотрим еще раз на наш код:

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

Например: мы хотим показать картинку на экране после ее загрузки, мы хотим что-то сделать с текстом прочитанного файла после его загрузки, мы хотим что-то сделать с результатом AJAX запроса после его выполнения и тп.

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

Исключительные ситуации

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

Из-за такого поведения мы легко можем попасть в неожиданную ловушку, например, вот так:

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

Источник

Полное понимание синхронного и асинхронного JavaScript с Async/Await

синхронный и асинхронный код. Смотреть фото синхронный и асинхронный код. Смотреть картинку синхронный и асинхронный код. Картинка про синхронный и асинхронный код. Фото синхронный и асинхронный код

По факту это адаптированный перевод двух отличных статей:

Выполнение синхронного и асинхронного кода в JavaScript

Недавно мы вели беседу с несколькими начинающими JS разработчиками, относительно того, как JS распределяет память и как парсится код, ну и само собой как он выполняется. Это одна из самых важных тем, которая никогда не являлась частью какой-либо программы обучения, но её, в принципе и не обязательно знать, чтобы написать программу на JavaScript. Такие темы очень важны для любопытных разработчиков, которые серьёзно относятся к своему делу. Я решил написать о ней, так как я нахожу её довольно неоднозначной, а люди имеют свойство сравнивать вещи, в особенности это склонны делать те, кто знаком с такими языками программирования как PHP, C++, Java и т.д, но учтите, что JavaScript это дикий зверь и с самого начала у меня он забрал довольно прилично времени для того, чтобы осознать некоторые важные аспекты, например то, как будучи однопоточным, JavaScript может быть синхронным и неблокируемым процессом?

Теперь перед тем как мы копнем глубже, давайте проясним основную концепцию и разницу между JavaScript Engine (движок) и JavaScript Run-time Environment.

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

С другой стороны, JavaScript Run-time Environment это среда, отвечающая за создание экосистемы с возможностями, сервисами и поддержкой, такими как массивы, функции, ключевые библиотеки и тп, которые необходимы для того, чтобы код запустился верно.

Функциональная модель

Почти все браузеры имеют на борту JavaScript движок. Самые популярные это V8 в Google Chrome и Node.js, SpiderMonkey от Мазилы, Chakra для IE и т.д. Хоть все эти браузеры и выполняют JavaScript по-разному, но под капотом, они все работают по старой доброй модели:

синхронный и асинхронный код. Смотреть фото синхронный и асинхронный код. Смотреть картинку синхронный и асинхронный код. Картинка про синхронный и асинхронный код. Фото синхронный и асинхронный код

Call Stack, Web APIs, Event loop, асинхронная очередь заданий, очередь на рендер и т.д. Все мы слышали эти шумные термины в нашей ежедневной работе. В совокупности, все они работают вместе, чтобы перевести и выполнить синхронные и асинхронные блоки кода, которые мы пишем каждый день. Давайте заглянем глубже в эту модель и попытаемся понять, что они делают и что самое важное — как они это делают.

Синхронные задачи

Что означает синхронность? Скажем, что у нас есть 2 строчки кода. Первая идет за второй. Синхронность означает то, что строка 2 не может запуститься до тех пор, пока строка 1 не закончит своё выполнение.

синхронный и асинхронный код. Смотреть фото синхронный и асинхронный код. Смотреть картинку синхронный и асинхронный код. Картинка про синхронный и асинхронный код. Фото синхронный и асинхронный код

Что происходит в Exercise 1: Итак, схема выше показывает нам типичное линейное выполнение кода. Когда код из трех console.log объявлений отдается в JS.

Шаг 1: console.log(«Print 1») отправляется в стек вызовов и выполняется, после того, как процесс завершится, он будет выкинут из стека. Теперь стек пуст и готов к следующим инструкциям на выполнение.

синхронный и асинхронный код. Смотреть фото синхронный и асинхронный код. Смотреть картинку синхронный и асинхронный код. Картинка про синхронный и асинхронный код. Фото синхронный и асинхронный код

Шаг 3: Функция Third() также отправляется в стек запросов и движок начинается её выполнение. Пока функции Second() и First() находятся в стеке и ждут своей очереди в соответствии с порядком.

Вот то, как браузер работает с синхронными задачами без привлечения чего-либо ещё, кроме “легендарного” стека вызовов. Но всё становится куда сложнее, когда JavaScript сталкивается с асинхронными задачами.

Асинхронные задачи

Что такое вообще — асинхронность? В отличие от синхронности, асинхронность это модель поведения. Предположим, что у нас есть две строчки кода, первая за второй. Первая строка это код которому нужно время. Итак, первая строка начинает запуск в фоновом режиме, позволяя второй строке запуститься без ожидания завершения первой строки.

Нам нужно такое поведение в случае, когда что-то подтормаживает и требует времени. Синхронность может казаться прямолинейной и незатейливой, но всё же может быть ещё и медленной. Такие задачи, как обработка изображений, операции с файлами, создание запросов сети и ожидание ответа — всё это может тормозить и быть долгим, производя огромные расчеты в 100 миллионов циклов итераций. Так что такие вещи в стеке запросов превращаются в “задержку”, ну или “blocking” по-английски. Когда стек запросов заблокирован, браузер препятствует вмешательству пользователя и выполнению другого кода до тех пор, пока “задержка” не выполнится и не освободит стек запросов. Таким образом асинхронные колбэки (callback) используются в таких ситуациях.

Пример: Видимо функция setTimeout() это простейший способ продемонстрировать основы асинхронного поведения.

синхронный и асинхронный код. Смотреть фото синхронный и асинхронный код. Смотреть картинку синхронный и асинхронный код. Картинка про синхронный и асинхронный код. Фото синхронный и асинхронный код

Exercise 3: Давайте рассмотрим стек запросов, который только что увидели:
Шаг 1: Как и обычно console.log(«Hello «) отправляется в стек первым и сразу же из него выкидывается после выполнения.

Шаг 2: setTimeout() отправляется в стек, но обратите внимание на то, что console.log(«Siddhartha») не может сразу выполниться, так как стоит отсрочка на 2 секунды. Так что пока эта функция для нас исчезнет, но мы позже разберем этот вопрос.

Шаг 4: Сейчас стек запросов пуст и в ожидании.

Шаг 5: Внезапно console.log( «Siddhartha» ) обнаруживается в стеке, после 2-х секунд задержки. Далее setTimeout() выполняется и сразу после этого выкидывается из стека. На 6-м шаге, наш стек оказывается пустым.

Это говорит о том, что пусть даже JavaScript и однопоточный, мы можем достичь согласованности действий через асинхронное исполнение задач.

Теперь у нас осталось несколько вопросов:

И тут появляется Event Loop (Или цикл обработки событий) и Web API. Давайте представим каждого из вышесказанных и ответим на эти три вопроса в нашей следующей схеме.

синхронный и асинхронный код. Смотреть фото синхронный и асинхронный код. Смотреть картинку синхронный и асинхронный код. Картинка про синхронный и асинхронный код. Фото синхронный и асинхронный код

Exercise 4: Давайте разберемся.
Шаг 2: С этого момента setTimeout(callback, 2000) отправляется в стек запросов. Как мы можем видеть, тут имеются компоненты callback и задержка в 2000ms. setTimeout() не является частью JavaScript движка, это по сути Web API включенное в среду браузера как дополнительный функционал.

Шаг 5: Теперь у нас есть callback в WebAPI, который собирается сработать по прошествии 2000ms. Но WebAPI не может напрямую как попало закидывать что-то в стек запросов, потому что это может создать прерывание для другого кода, выполняемого в JavaScript движке, именно в этот момент. Так что callback поставится в очередь выполнения задач после 2000ms. А теперь WebAPI пуст и свободен.

Шаг 6: Цикл событий или Event Loop — ответственный за взятие первого элемента из очереди задач и передачу его в стек запросов, только тогда, когда стек пуст и свободен. На этом шаге нашего уравнения, стек запросов пуст.

Шаг 7: Итак, callback отправлен в стек запросов, так как он был пуст и свободен. И тут же выполнился. Так что ответ на второй вопрос готов.

Шаг 9: После того, как console.log(«Siddhartha») выполнен, он выкидывается из стека запросов и JavaScript приходит к завершению выполнения callback. Который в свою очередь после своего завершения будет выкинут из стека запросов. А вот и ответ на вопрос как.

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

Теперь давайте посмотрим на пример с Async/Await.

Итак, вы знаете что он делает, но знаете ли вы как?

У большинства разработчиков неоднозначное отношение к JavaScript, отчасти из-за того, что они становятся жертвами одного из его лучших качеств: он легко учится, но тяжело применяется. Это легко подметить взглянув на то, сколько разработчиков склонны полагать, что этот язык работает сугубо однопоточно, но на самом деле всё происходит по-другому если взглянуть под капот. Именно эта разница проявляется в деталях и вызывает разочарование.

Для примера, я не сомневаюсь в том, что изменения в стандартах вызвали у многих из вас недопонимание о поведении языка, например как с классами. В JavaScript нет классов, в реальности JavaScript использует Prototypes, синглтон объекты, из которых наследуются другие объекты. По факту, все объекты в JavaScript имеют прототип из которого они наследуются. Это означает то, что классы в JS на самом деле не ведут себя как классы. Класс это схема для создания экземпляров объекта, а prototype это экземпляр, которому другие экземпляры объекта передают работу, prototype это не схема и не шаблон, он просто есть и всё.

Именно поэтому вы можете добавить новый метод для Array и тут же все массивы смогут его использовать. Это можно сделать в среде выполнения, затронув объект, ставший экземпляром.

В общем, классы в JavaScript это синтаксический сахар для прототипизирования.

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

Async/Await спецификации

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

async/await пытается решить одну из главных головных болей языка со времен его появления: это асинхронность. То, как работает концепция асинхронного кода, вы прочитали в первой части этой статьи, если вы ещё не поняли, то обязательно перечитайте и поймите перед тем как читать дальше.

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

Всё это хорошо, но что если мы столкнемся с последовательностью?

То, что вы видите выше иногда называется Pyramid of Doom и Callback Hell.

Узрите: промисы

Промисы это очень мудрый и хороший способ работы с асинхронным кодом.

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

Промис это объект, который представляет собой асинхронный таск, который должен завершиться. При использовании это выглядит как-то так:

buyCoffee возвращает промис, который является процессом покупки кофе. Функция resolve указывает промису на то, что он выполнен. Он получает значение как аргумент, который будет доступен в промисе позже.

В самом экземпляре промиса есть два основных метода:

then — запускает колбек, который вы передали, когда промис завершен.

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

Итак, мы уже почти подошли к самому интересному.

Как это работает

Ваш код может встать на паузу в ожидании Async функции с await

Await возвращает то, что асинхронная функция отдаёт при завершении.

Await может быть использовано только внутри async функции.

Давайте посмотрим на код:

Итак, теперь doManyThings() это тоже асинхронная функция, как нам ожидать её? Да никак. Не с нашим новым синтаксисом. У нас есть три варианта:

3. Или используйте как промис.

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

Простая async функция:

Async функция, которая ожидает результат другой async функции:

Пример, на который важно обратить внимание
Вот пример того, почему понимание того, как работает async/await реально важно.

Пример выше выдаст синтаксическую ошибку, так как мы передаем forEach синхронную функцию.

Не проблема, верно? Нам всего-лишь надо передать ей async функцию. А вот и нет.

Что тут не так? Давайте посмотрим на то, как это интерпретируется. Я не будут многословным с промисами и объясню максимально просто то, как это было бы в случае с ними:

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

И есть один, который будет. Это современная версия цикла for, “ for of ”, которая понимает await для промисов.

Если вы не можете использовать “ for of ”, то вы можете применить итератор, который поддерживает промисы или использовать библиотеку, такую как bluebird Promise.each

Заключение

Я надеюсь, что это прояснило картину, async/await это просто, если вы хорошо понимаете промисы.

синхронный и асинхронный код. Смотреть фото синхронный и асинхронный код. Смотреть картинку синхронный и асинхронный код. Картинка про синхронный и асинхронный код. Фото синхронный и асинхронный кодсинхронный и асинхронный код. Смотреть фото синхронный и асинхронный код. Смотреть картинку синхронный и асинхронный код. Картинка про синхронный и асинхронный код. Фото синхронный и асинхронный кодсинхронный и асинхронный код. Смотреть фото синхронный и асинхронный код. Смотреть картинку синхронный и асинхронный код. Картинка про синхронный и асинхронный код. Фото синхронный и асинхронный код

синхронный и асинхронный код. Смотреть фото синхронный и асинхронный код. Смотреть картинку синхронный и асинхронный код. Картинка про синхронный и асинхронный код. Фото синхронный и асинхронный код

Email подписка!

Источник

Синхронность и асинхронность процессов

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

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

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

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

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

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

Теперь представим – в нашей информационной системе не подключен сервис оценки поставщиков. Значит, юридическому отделу нужно собирать информацию из открытых источников. Значит, на выполнение оценки требуется время. С учетом очереди заявок к юристам, пройдет дня три.

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

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

Конечно, юристы могут возмутиться – чего это мы будем оценивать поставщика, если вы там еще четко не решили, будете ли у него заказывать? Что им ответить?

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

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

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

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

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

Типичный пример: «я буду согласовывать только после того, как согласует вот он». Или «я посмотрю на этот договор только после финансистов». Хотя, если верить статистике и здравому смыслу, подобные постановки не имеют под собой оснований, и являются лишь способом переложить ответственность.

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

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

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

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

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

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

Кроме асинхронности, есть еще более мощный метод оптимизации – буферизация процессов. О нем – в другой раз.

Источник

Введение в асинхронный JavaScript

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

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

Синхронный JavaScript

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

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

В этом блоке кода команды выполняются одна за другой:

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

Так и в примере выше: после нажатия кнопки абзац не сможет появиться пока не будет нажата кнопка OK в окне сообщения. Попробуйте сами:

Асинхронный JavaScript

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

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

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

Есть два типа стиля асинхронного кода, с которыми вы столкнётесь в коде JavaScript, старый метод — колбэки (callbacks) и более новый — промисы (promises). В следующих разделах мы познакомимся с каждым из них.

Асинхронные колбэки

Асинхронные колбэки — это функции, которые определяются как аргументы при вызове функции, которая начнёт выполнение кода на заднем фоне. Когда код на заднем фоне завершает свою работу, он вызывает колбэк-функцию, оповещающую, что работа сделана, либо оповещающую о трудностях в завершении работы. Обратные вызовы — немного устаревшая практика, но они все ещё употребляются в некоторых старомодных, но часто используемых API.

Пример асинхронного колбэка вторым параметром addEventListener() (как мы видели выше):

Первый параметр — тип обрабатываемого события, второй параметр — колбэк-функция, вызываемая при срабатывании события.

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

Вы можете написать свою собственную функцию, содержащую колбэк-функцию. Давайте взглянем на ещё один пример, в котором происходит загрузка ресурсов через XMLHttpRequest API (запустите пример, и посмотрите исходный код):

Заметьте, что не все колбэк-функции асинхронны — некоторые запускаются синхронно. Например, при использовании Array.prototype.forEach() для перебора элементов массива (запустите пример, и посмотрите исходный код):

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

Промисы

Примечание: вы можете посмотреть законченную версию на github (посмотрите исходный код и запустите пример).

В примере видно, как fetch() принимает один параметр — URL ресурса, который нужно получить из сети, — и возвращает промис. Промис — это объект, представляющий асинхронную операцию, выполненную удачно или неудачно. Он представляет собой как бы промежуточное состояние. По сути, это способ браузера сказать: «я обещаю вернуться к вам с ответом как можно скорее», поэтому в дословном переводе «промис» (promise) означает «обещание».

Может понадобиться много времени, чтобы привыкнуть к данной концепции; это немного напоминает Кот Шрёдингера в действии. Ни один из возможных результатов ещё не произошёл, поэтому операция fetch в настоящее время ожидает результата. Далее у нас есть три блока кода следующих сразу после fetch() :

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

Очередь событий

Промисы и колбэк-функции

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

Тем не менее, промисы сделаны специально для обработки асинхронных операций, и имеют много преимуществ по сравнению с колбэками:

Природа асинхронного кода

Давайте рассмотрим пример, который дополнительно иллюстрирует природу асинхронного кода, показывая, что может произойти, когда мы не полностью осознаем порядок выполнения кода, и проблемы, связанные с попыткой трактовать асинхронный код как синхронный. Следующий пример довольно похож на тот, что мы видели раньше (запустите пример, и посмотреть исходный код). Одно из отличий состоит в том, что мы включили ряд операторов console.log() чтобы проиллюстрировать порядок, в котором, как вы думаете, будет выполняться код.

Если вы запутались, рассмотрим следующий небольшой пример:

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

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

Чтобы увидеть это в действии, попробуйте взять локальную копию нашего примера и измените третий вызов console.log () следующим образом:

Теперь вместо третьего сообщения должна возникнуть следующая ошибка:

Примечание: Из соображений безопасности вы не можете применять fetch() к файлам из вашей локальной системы (или запустить другие такие операции локально); чтобы запустить локально пример выше вам необходимо запустить его через локальный веб-сервер.

Активное обучение: сделайте все это асинхронно!

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

Заключение

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

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

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

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

Источник

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

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