Асинхронный запрос что это
Перейти к содержимому

Асинхронный запрос что это

  • автор:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  • Анализ и проектирование систем
  • Управление персоналом

Синхронное и асинхронное

По загадочным причинам, в сфере вычислительной техники слово «асинхронный» означает «синхронный». Например, «асинхронный запрос» — это когда синхронно (т. е. одновременно) с ним может выполняться другой. А вот если синхронность в выполнении отсутствует, и запросы выполняются последовательно друг за другом, то их с какого-то бодуна называют «синхронными».

Бывает, сделаешь страничку, а медленный скрипт блокирует её загрузку. Дописываешь в нужное место параметр async — и проблема решена, теперь страница и скрипт грузятся синхронно! У вас не взрывается от этого мозг?

У меня — в клочья.

Поделиться
Поделиться

38 комментариев
Илья Миськов 2011
Дмитрий 2011

Дизайнер никогда не поймет логику программиста, и наоборот.
(Ну Чикуёнок разве что)

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

Игорь 2011

Нет. Просто у программистов иная точка отсчета что называть синхронным и асинхронным

Илья Бирман 2011

Значение слова в словаре посмотреть перед тем, как точку отсчёта устанавливать — не вариант?

Илья Миськов 2011

Дмитрий, не понял тебя.

Павел Власов 2011

Кто станет объяснять «почему это так и не могло быть иначе» — тот программист! А мне понравился новый взгляд на привычные вещи 🙂

Leonid 2011

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

Илья Бирман 2011

Я конечно понимаю, что одновременность — понятие относительное, но какое релятивистские эффекты имеют отношение к этому вопросу — для меня загадка.

Юрий Корчёмкин 2011

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

Виктор Глушенков 2011

Ни от каких точек зрения или технического образования это не зависит. Синхронное исполнение музыкальных партий (к примеру) и параллельное — не одно и то же. Кому как не дискжокею это знать.
Вообще похоже на троллинг в духе «опенсоса» в день программиста))

Денис Попов 2011

Илья, вы путаете.

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

Википедия сообщает нам: «In computer science, especially parallel computing, synchronization refers to the coordination of simultaneous threads or processes to complete a task; in order to obtain correct runtime order and avoid unexpected race conditions».

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

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

Синхронные и асинхронные запросы

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

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

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

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

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

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

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

Такой способ обработки асинхронного запроса называется callback или обратный вызов.

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

Этот способ обработки называется event-driven, т. е. управляемый событиями.

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

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

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

Если же доставка осуществляется курьером, то мы на заключительном этапе ожидаем прибытия курьера по указанному адресу (ждем события). [1] [2]

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

А как же быть с клиентом? Для реализации клиента, возможно, в ряде случаев однопоточной архитектуры может быть и достаточно. Можно назвать ряд условий, при которых многопоточная архитектура и на клиенте будет предпочтительнее, например, если будет использоваться механизм асинхронных запросов клиента к серверу, или если клиент взаимодействует с несколькими серверами [3] .

  • [1] На практике легче всего реализуется метод опроса, но за удобствоприходится платить неэффективностью использования ресурсов вычислительной системы. Наиболее удобен для вычислительной системыметод ожидания события, обычно он расходует наименьшее количестворесурсов. Метод обратного вызова соединяет худшие свойства обоих методов и на практике чаще всего сводится к усложненному методу опроса. Мы рассматривали только реакцию процесса на асинхронный запрос, т. е. подразумевалось, что нормальное функционирование процесса, выдавшего запрос, будет продолжено после получения отклика, т. е.ответа на запрос. По сути, мы пытаемся синхронизировать процессы —выдавший запрос и отвечающий на запрос. В распределенных системахтакой режим, конечно, возможен и важен. Мы не рассмотрели еще один очень важный метод обработки асинхронных запросов. Основная идея этого метода в том, что мы не пытаемся каким-либо образом связать деятельность выдавшего процессас выданным запросом, по крайней мере, немедленно. Мы изменим состояние процесса, контекст, в тот момент, когда придет сообщение,возможно, связанное с запросом (а возможно, и не связанное). Такойрежим функционирования называют message-driven режимом (режимом, управляемым сообщениями), он крайне важен для функционирования большого количества подсистем в распределенных системах,и мы подробнее ознакомимся с ним немного позднее. Логика функционирования серверной части наводит нас на мысли,что: • клиентов может быть много;
  • [2] клиентов надо обслуживать быстро, не нужно устраивать ненужных очередей; • клиентов можно обслуживать параллельно, задействуя для этоговсе доступные исполнительные устройства; • при обслуживании клиентов неизбежно возникнет такая ситуация, когда для исполнения запроса потребуется одновременный доступк общим данным. Все это говорит нам, что серверную сторону крайне желательно реализовывать в среде, допускающей параллельное исполнение кода в разных процессах или вычислительных потоках.
  • [3] По правде говоря, в современных (2018 г.) вычислительных системах весьма трудно найти архитектурное решение, операционную систему, которая могла бы применяться в распределенных системах и в которой не было бы поддержки вычислительных потоков и средств синхронизации между ними.

Синхронные и асинхронные запросы

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

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

Асинхронные запросы

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

Пример: отправка запроса и получение файла ответа

Приведём простейший пример асинхронного запроса XMLHttpRequest .

var xhr = new XMLHttpRequest(); xhr.open("GET", "/bar/foo.txt", true); xhr.onload = function (e)  if (xhr.readyState === 4)  if (xhr.status === 200)  console.log(xhr.responseText); > else  console.error(xhr.statusText); > > >; xhr.onerror = function (e)  console.error(xhr.statusText); >; xhr.send(null); 

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

3 строка. Создаётся функция обработчик события onload . Этот обработчик следить за параметром readyState , для того, чтобы определить завершена ли передача данных и если это так и HTTP статус 200, то полученные данные выводятся в консоль. А если в результате передачи данных возникла ошибка, то сообщение об ошибки будет выведено в консоль.

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

Пример: создание стандартной функции для чтения внешних файлов

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

function xhrSuccess()  this.callback.apply(this, this.arguments); > function xhrError()  console.error(this.statusText); > function loadFile(url, callback /*, opt_arg1, opt_arg2, . */)  var xhr = new XMLHttpRequest(); xhr.callback = callback; xhr.arguments = Array.prototype.slice.call(arguments, 2); xhr.onload = xhrSuccess; xhr.onerror = xhrError; xhr.open("GET", url, true); xhr.send(null); > 
function showMessage(message)  console.log(message + this.responseText); > loadFile("message.txt", showMessage, "New message!\n\n"); 

Сигнатура вспомогательной функции LoadFile следующая: 1 аргумент — URL адрес для запроса (через HTTP GET), 2 аргумент — функция, которая будет вызвана после успешного выполнения ajax запроса и 3 аргумент — список аргументов, которые будут передаваться через XHR объект в функцию, которая была указана во 2 аргументе.

Строка 1 определяет функцию, которая будет вызвана, когда ajax запрос завершиться успешно. В свою очередь это вызовет функции callback, которая была указана в вызове функции loadFile (то есть функция showMessage ) которая была обозначена как свойство XHR объекта (строка 11). Дополнительные аргументы, которые были указаны при вызове функции loadFile , подставляются в вызов callback функции.

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

Строка 11 сохраняет в XHR объекте функцию, которая будет вызвана после успешного завершения ajax запроса. (эта функция передаётся 2 аргументов в вызове функции loadFile ).

12 строка срезает псевдомассив аргументов, который был передан при вызове функции loadFile . Начиная с 3 аргумента все аргументы будут хранится в массиве arguments объекта xhr , который передаётся в функцию xhrSuccess и в конечном итоге будут использованы при вызове функции showMessage , которая будет вызвана функцией xhrSuccess .

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

Строка 16 инициализирует запрос.

Пример: использование timeout

При использовании асинхронных запросов, можно установить максимальное время ожидания ответа от сервера. Это делается путём установки значения свойства timeout XMLHttpRequest объекта, как показано ниже:

function loadFile(url, timeout, callback)  var args = Array.prototype.slice.call(arguments, 3); var xhr = new XMLHttpRequest(); xhr.ontimeout = function ()  console.error("The request for " + url + " timed out."); >; xhr.onload = function ()  if (xhr.readyState === 4)  if (xhr.status === 200)  callback.apply(xhr, args); > else  console.error(xhr.statusText); > > >; xhr.open("GET", url, true); xhr.timeout = timeout; xhr.send(null); > 

Отметим, что в код была добавлена функция обработчик события ontimeout .

function showMessage(message)  console.log(message + this.responseText); > loadFile("message.txt", 2000, showMessage, "New message!\n"); 

2 аргумент функции loadFile устанавливает время ожидание равное 2000ms.

Примечание: Поддержка timeout была добавлена начиная с Gecko 12.0.

Synchronous request

Примечание: Starting with Gecko 30.0, Blink 39.0, and Edge 13, synchronous requests on the main thread have been deprecated due to the negative effects to the user experience.

Synchronous XHR often causes hangs on the web. But developers typically don’t notice the problem because the hang only manifests during poor network conditions or slow server response. Synchronous XHR is now in deprecation state. Developers are recommended to move away from the API.

All new XHR features such as timeout or abort aren’t allowed for synchronous XHR. Doing so would invoke InvalidAccessError .

Example: HTTP synchronous request

This example demonstrates how to make a simple synchronous request.

var request = new XMLHttpRequest(); request.open("GET", "/bar/foo.txt", false); // `false` makes the request synchronous request.send(null); if (request.status === 200)  console.log(request.responseText); > 

Line 3 sends the request. The null parameter indicates that no body content is needed for the GET request.

Line 5 checks the status code after the transaction is completed. If the result is 200 — HTTP’s «OK» result — the document’s text content is output to the console.

Example: Synchronous HTTP request from a Worker

One of the few cases in which a synchronous request does not usually block execution is the use of XMLHttpRequest within a Worker .

example.html (the main page):

doctype html> html> head> meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> title>MDN Exampletitle> script type="text/javascript"> var worker = new Worker("myTask.js"); worker.onmessage = function (event)  alert("Worker said: " + event.data); >; worker.postMessage("Hello"); script> head> body>body> html> 

myFile.txt (the target of the synchronous XMLHttpRequest invocation):

Hello World!!

myTask.js (the Worker ):

.onmessage = function (event)  if (event.data === "Hello")  var xhr = new XMLHttpRequest(); xhr.open("GET", "myFile.txt", false); // synchronous request xhr.send(null); self.postMessage(xhr.responseText); > >; 

Примечание: The effect, because of the use of the Worker , is however asynchronous.

It could be useful in order to interact in the background with the server or to preload some content. See Using web workers for examples and details.

Adapting Sync XHR usecases to the Beacon API

There are some cases in which the synchronous usage of XMLHttpRequest was not replaceable, like during the window.onunload and window.onbeforeunload events. You should consider using the fetch API with keepalive flag. When fetch with keepalive isn’t available, you can consider using the navigator.sendBeacon API can support these use cases typically while delivering a good UX.

The following example (from the sendBeacon docs) shows a theoretical analytics code that attempts to submit data to a server by using a synchronous XMLHttpRequest in an unload handler. This results in the unloading of the page to be delayed.

.addEventListener("unload", logData, false); function logData()  var client = new XMLHttpRequest(); client.open("POST", "/log", false); // third parameter indicates sync xhr. :( client.setRequestHeader("Content-Type", "text/plain;charset=UTF-8"); client.send(analyticsData); > 

Using the sendBeacon() method, the data will be transmitted asynchronously to the web server when the User Agent has had an opportunity to do so, without delaying the unload or affecting the performance of the next navigation.

The following example shows a theoretical analytics code pattern that submits data to a server by using the sendBeacon() method.

.addEventListener("unload", logData, false); function logData()  navigator.sendBeacon("/log", analyticsData); > 

See also

  • XMLHttpRequest
  • Using XMLHttpRequest
  • AJAX
  • navigator.sendBeacon

Found a content problem with this page?

  • Edit the page on GitHub.
  • Report the content issue.
  • View the source on GitHub.

This page was last modified on 3 авг. 2023 г. by MDN contributors.

Your blueprint for a better internet.

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

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