Принудительно перерисовывать пользовательский интерфейс в Webkit (Safari & Chrome) прямо перед синхронным запросом Ajax - PullRequest
7 голосов
/ 07 ноября 2011

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

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

load:function(url) {
    ....
    busyMask.className="Shown"; //display=block; absolute positioned full screen semi-transparent
    var dta=$.ajax({type:"GET",dataType:"json",url:url,async: false}).responseText;
    busyMask.className="Hidden"; //sets display=none;
    ...
    return JSON.parse(dta);
    }

Хорошо известно, что синхронные запросы блокируют пользовательский интерфейс. Поэтому неудивительно, что белое наложение никогда не появляется в Safari и Chrome (это интересно в Firefox). Я попытался замедлить ответ и использовать розовое наложение, чтобы оно было до боли очевидным, но оно не будет обновлять пользовательский интерфейс до тех пор, пока запрос не будет завершен. Если оставить часть «busyMask.className =" Hidden "", будет показана маска - но только после завершения запроса ajax.

Я видел много уловок, заставляющих пользовательский интерфейс перерисовывать (например, Почему HourGlass не работает с синхронным запросом AJAX в Google Chrome? , http://ajaxian.com/archives/forcing-a-ui-redraw-from-javascript),, но все они, похоже, находятся в в сочетании с попыткой показать фактическое «постоянное» DOM или обновления стилей, а не с временным отображением изменения стиля при выполнении синхронного запроса.

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

Ответы [ 4 ]

6 голосов
/ 10 ноября 2011

Хорошо, для целей этого вопроса я проигнорирую обоснование того, почему вам требуются синхронные запросы XHR.Я понимаю, что иногда рабочие ограничения не позволяют использовать лучшие практические решения, и поэтому мы «справляемся», чтобы выполнить работу.Итак, давайте сосредоточимся на том, как получить синхронный ajax с визуальным обновлением, работающим на вас!

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

Сначала давайте настроим индикатор загрузки в нашей разметке:

<div id="loading" style="display:none;">Loading...</div>

Теперь давайте создадим некоторый JavaScript:

// Show the loading indicator, then start a SYNCRONOUS ajax request
$('#loading').show().delay(100).queue(function() {
    var jqxhr = $.ajax({type:"GET",dataType:"json",url:"www.yoururl.com/ajaxHandler",async: false}).done(function(){
        //Do your success handling here
    }).fail(function() {
        //Do your error handling here
    }).always(function() {
        //This happens regardless of success/failure
        $('#loading').hide();
    });
    $(this).dequeue();
});

СначалаМы хотим показать наш индикатор загрузки, а затем дать браузеру небольшую задержку для перерисовки, прежде чем начнется наш синхронный XHR-запрос.Используя метод jQuery .queue(), мы помещаем наш вызов .ajax() в очередь fx по умолчанию, чтобы он не выполнялся до тех пор, пока не завершится .delay(), что, конечно, не произойдет, пока не завершится .show().

Метод jQuery .show() изменяет стиль отображения CSS целевого элемента на блокированный (или восстанавливает его начальное значение, если назначено).Это изменение в CSS заставит браузер перекомпоноваться (он же «перерисовать»), как только сможет.Задержка гарантирует, что он сможет перекомпоноваться перед вызовом Ajax.Задержка не обязательна во всех браузерах, но не повредит больше, чем указанное вами количество миллисекунд (как обычно, IE будет ограничивающим фактором, другие браузеры довольны задержкой в ​​1 мс, IE хотел чего-то немногоболее важно перерисовать).

Вот jsfiddle для тестирования в нескольких браузерах: Пример jsfiddle

0 голосов
/ 18 июля 2013
$.ajaxSetup({async:false});

    busyMask.className="Shown"; //display=block; absolute positioned full screen semi-transparent
    var dta=$.ajax({type:"GET",dataType:"json",url:url}).responseText;
    busyMask.className="Hidden"; //sets display=none;

$.ajaxSetup({async:true});
0 голосов
/ 15 ноября 2011

Я выполнил несколько тестов и получил несколько баллов: http://jsfiddle.net/xVHWs/1/

Измените ваш код, чтобы использовать hide(), show() или animate({ opacity: 'show' }, 'fast') и animate({ opacity: 'hide' }, 'fast') в jQuery, если вы покинете функциибез указания времени или указания времени 0 мс, Firefox покажет наложение и скроет его, другие браузеры выполнят его быстро, чтобы вы увидели.Введите 100 миллисекунд в вызовы show, hide или animate, и вы увидите это.

0 голосов
/ 11 ноября 2011

Почему вы думаете:

doSomethingBeforeRequest();
response = synchronousAjax();
doSomethingToTheDataAfterRequest(response);

намного «проще», чем:

doSomethingBeforeRequest();
properAjax(onSuccess(response){
   doSomethingToTheDataAfterRequest(response);
};

для вашей команды?Я не пытаюсь спорить, но мне серьезно любопытно оправдание ...

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

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

РЕДАКТИРОВАТЬ (какой-то ответ):

СВ JavaScript нет потоков;Тайм-ауты и ajax-вызовы (которые позволяют браузеру делать что-то еще до того, как он будет запущен; в некотором роде используется sleep () на многопоточном языке), достаточно важны для программирования JavaScript.Я знаю, что поначалу это может быть чем-то вроде кривой обучения (я знаю, что был сбит с толку), но на самом деле нет никакого разумного способа избежать его изучения.

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

doSomethingBeforeRequest1();
ajax(onSuccess(response1){
   doSomethingToTheDataAfterRequest1(response1);
   ajax(onSuccess(response2){
       doSomethingToTheDataAfterRequest2(response2);
   };
};

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

(* Я более удивлен, что Firefox обновляет домен ...)

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