Есть ли причина использовать синхронный XMLHttpRequest?

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

18 ответов


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


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

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


обновление:

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

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

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

As указано в документах Mozilla есть случаи, когда вы должны использовать синхронные запросы; однако также перечислены обходной путь, который использует Маяк (недоступно в IE/Safari) для таких случаев. Хотя это экспериментально, если он когда-либо достигнет принятия стандартов, он может, возможно, положить гвоздь в гроб синхронного запроса.


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

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

другой причиной было бы при работе с веб-сервисом, особенно при выполнении математики на сервере.

пример: сервер имеет переменную со значением 1.

 Step (1) Perform Update: add 1 to variable
 Step (2) Perform Update: set variable to the power of 3
 End Value: variable equals 8

Если Шаг (2) происходит первым, то конечное значение равно 2, а не 8; таким образом, порядок операций и синхронизация необходимы.


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

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

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


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

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


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

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

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


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

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

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

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

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


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


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


Что произойдет, если вы сделаете синхронный вызов в производственном коде?

небо падает.

Нет серьезно, пользователю не нравится заблокированный браузер.


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

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

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


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

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


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

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


причина:

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

очевидно, вы хотите, чтобы это срабатывало от onload.

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

недостаток:

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

альтернатива?

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

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

http://www.w3.org/TR/2012/WD-XMLHttpRequest-20120117/#the-open-method делает его похожим (см. раздел 4.7.3), вам не разрешено устанавливать тайм-аут при использовании блокировки режим. Кажется мне интуитивно понятным: всякий раз, когда блокируется IO, вежливо установить разумный тайм-аут, так почему разрешать блокировку io, но не с заданным пользователем тайм-аутом?

мое мнение заключается в том, что блокировка ввода-вывода имеет жизненно важную роль в некоторых ситуациях, но должна быть реализована правильно. Хотя для одной вкладки или окна браузера недопустимо блокировать все другие вкладки или окна, это недостаток дизайна браузера. Стыд там, где должен быть стыд. Но это вполне приемлемо в некоторых случаях для индивидуальная вкладка или окно не реагируют в течение нескольких секунд (т. е. с помощью блокировки ввода-вывода/HTTP GET) в некоторых ситуациях-например, при загрузке страницы, возможно, много данных должно быть, прежде чем что-либо может быть сделано в любом случае. Иногда правильно реализованный блокирующий код является самым чистым способом сделать это.

конечно, эквивалентная функция в этом случае может быть получена с помощью асинхронного http gets, но какая глупая процедура требуется?

думаю, я бы попробовал что-то вдоль этих линий:

при загрузке документа, выполните следующие действия: 1. Настройка 6 глобальных "сделали" флаг переменные равны 0. 2. выполнение всех 6 фон получит (при условии заказа не имеет значения)

затем обратные вызовы завершения для каждого из 6 HTTP get будут устанавливать свои соответствующие флаги "готово". Кроме того, каждый обратный вызов будет проверять все другие флаги done, чтобы увидеть, завершены ли все 6 HTTP. Последний обратный вызов, который нужно завершить, увидев, что все остальные завершены, будет затем вызовите реальную функцию инициализации, которая затем настроит все, теперь, когда все данные были извлечены.

Если порядок выборки имел значение - или если веб-сервер не смог принять несколько запросов одновременно - тогда вам понадобится что-то вроде этого:

в onload () будет запущен первый HTTP get. В его callback будет запущен второй. В нем обратный вызов, третий-и так далее и тому подобное, с каждым обратным вызовом, запускающим следующий HTTP ПОЛУЧИТЬ. Когда последний вернется, он вызовет реальную процедуру init ().


XMLHttpRequest традиционно используется для асинхронных запросов. Иногда (для отладки или конкретной бизнес-логики) вы хотели бы изменить все/несколько асинхронных вызовов на одной странице для синхронизации.

Вы хотели бы сделать это, не меняя все в JS коде. Флаг async/sync дает вам эту возможность, и если он разработан правильно, вам нужно изменить только одну строку в коде/изменить значение one var во время выполнения.


Firefox (и, вероятно, все браузеры, отличные от IE) не поддерживает async xhr timeOut.

  1. обсуждение Stackoverflow
  2. Mozilla Firefox XMLHttpRequest

в HTML5 WebWorkers поддерживает тайм-ауты. Таким образом, вы можете обернуть запрос синхронизации XHR к WebWorker с таймаутом для реализации асинхронного XHR с поведением таймаута.


У меня просто была ситуация, когда асинхронные запросы для списка URL-адресов, вызываемых последовательно с помощью forEach (и цикла for), привели бы к отмене оставшихся запросов. Я переключился на синхронный режим, и они работают по назначению.


SYNC vs ASYNC: в чем разница?

в основном это сводится к следующему:

console.info('Hello, World!');
doSomething(function handleResult(result) {
    console.info('Got result!');
});
console.info('Goodbye cruel world!');

, когда doSomething is синхронно это печатать:

Hello, World!
Got result!
Goodbye cruel world!

напротив, если doSomething is асинхронные, это печатать:

Hello, World!
Goodbye cruel world!
Got result!

потому что функция doSomething делает это асинхронно, он возвращается до того, как работа будет выполнена. Таким образом, мы получаем результат только после печати Goodbye cruel world!

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

console.info('Hello, World!');
doSomething(function handleResult(result) {
    console.info('Got result!');
    if (result === 'good') {
        console.info('I feel great!');
    }
    else {
        console.info('Goodbye cruel world!');
    }
});

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

ЗАЧЕМ ИСПОЛЬЗОВАТЬ СИНХРОННЫЙ XMLHTTPREQUEST?

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

function lives(name) {
    return (name !== 'Elvis');
}
console.info('Elvis ' + (lives('Elvis') ? 'lives!' : 'has left the building...');

предположим, что у нас нет контроля над вызывающим кодом (console.info line) и нужно изменить функцию lives спросить сервер... Мы не можем сделать асинхронный запрос на сервер изнутри lives и все еще есть наш ответ до lives завершается. Так что мы не знаем, возвращаться ли нам true или false. Единственный способ получить результат до завершения функции-это выполнить синхронный запрос.

As Sami Samhuri упоминает в своем ответе, очень реальный сценарий, где вам может понадобиться ответ на ваш запрос сервера, прежде чем ваша функция завершится, - это onbeforeunload событие, так как это последняя функция из вашего приложения, которая будет работать до закрытия окна.

МНЕ НЕ НУЖНЫ ВЫЗОВЫ СИНХРОНИЗАЦИИ, НО Я ВСЕ РАВНО ИХ ИСПОЛЬЗУЮ, ТАК КАК ОНИ ПРОЩЕ

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

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

var carRented = rentCar().then(function(car){
  gasStation.refuel(car);
});

var hotelBooked = bookHotel().then(function(reservation) {
  reservation.confirm();
});

Promise.all([carRented, hotelBooked]).then(function(){
  // At this point our car is rented and our hotel booked.
  goOnHoliday();
});

вот как вы бы реализовали bookHotel:

function bookHotel() {
  return new Promise(function(resolve, reject){
    if (roomsAvailable()) {
      var reservation = reserveRoom();
      resolve(reservation);
    }
    else {
      reject(new Error('Could not book a reservation. No rooms available.'));
    }
  });
}

Читайте также: написать лучше JavaScript с обещаниями.


Ну вот одна веская причина. Я хотел сделать http-запрос, а затем, в зависимости от результата, вызвать click() на входной тип=файл. Это невозможно с асинхронным xhr или fetch. Обратный вызов теряет контекст "действие пользователя", поэтому вызов click() игнорируется. Синхронный xhr спас мой бекон.

onclick(event){
    //here I can, but I don't want to.
    //document.getElementById("myFileInput").click();
    fetch("Validate.aspx", { method : "POST", body: formData, credentials: "include" })
    .then((response)=>response.json())
    .then(function (validResult) {
        if (validResult.success) {
            //here, I can't.
            document.getElementById("myFileInput").click();
        }
    });
}