Синхронный / асинхронный характер рендеринга браузера и выполнения JavaScript - PullRequest
6 голосов
/ 23 марта 2012

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

.processing
{
  background-color: #ff0000;
}

<div id="mydiv">
  Processing
</div>

Сценарий:

$("#mydiv").addClass("processing");
// Do some long running processing
$("#mydiv").removeClass("processing");

Я наивно думал, что класс будет применен к div, и пользовательский интерфейс будет обновлен. Тем не менее, при запуске этого в браузере (по крайней мере, в Firefox) div никогда не выделяется. Может кто-нибудь объяснить мне, почему мой div никогда не выделяется? Класс добавляется, происходит обработка, а затем класс удаляется; пользовательский интерфейс не обновляется в промежутке, и пользователь никогда не видит красный фон.

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

И что более важно, каков рекомендуемый способ достижения этого эффекта? Должен ли я использовать setTimeout, чтобы медленная обработка была асинхронной и работать с обратным вызовом? Должен быть лучший способ, потому что мне действительно не нужно асинхронное поведение; Я просто хочу, чтобы интерфейс обновился.

EDIT:

JSFiddle: http://jsfiddle.net/SE8wD/5/

(Обратите внимание, что вам может понадобиться настроить количество итераций цикла, чтобы обеспечить разумную задержку на вашем собственном ПК)

Ответы [ 4 ]

1 голос
/ 23 марта 2012

Я не рекомендую замораживать браузер для весового скрипта.В этом случае я предпочитаю менять код для не зависания браузера и DOM.Если в вашем коде есть какой-то цикл, вы можете вызвать функцию с задержкой (используя setInterval) и где-то сохранить некоторую переменную состояния.Например:

for(var i=0; i<1000000; i++) {
    // do something
}

Может быть:

var i = 0;
var intervalID;
var _f = function() {
    // do something
    i++;
    if(i==1000000) clearInterval(intervalID);
}
intervalID  = setInterval(_f,1);

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

1 голос
/ 23 марта 2012

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

$("#mydiv").addClass("processing");
setTimeout(function(){
   // This runs as a seperate event after
   // Do some long running processing
   $("#mydiv").removeClass("processing");
},1);

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

0 голосов
/ 23 марта 2012

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

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

0 голосов
/ 23 марта 2012

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

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