2012-03-23 9 views
6

Mam pewne przetwarzanie, które potrwa kilka sekund, więc chcę dodać wskaźnik wizualny, gdy jest on w toku.Synchroniczny/asynchroniczny charakter renderowania przeglądarki i wykonywania javascript

.processing 
{ 
    background-color: #ff0000; 
} 

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

Scenariusz:

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

ja naiwnie myślałem, że klasa będzie stosowana do div i interfejs użytkownika będzie aktualizowana. Jednak uruchamianie tego w przeglądarce (przynajmniej w Firefoksie) div nigdy nie jest podświetlone. Czy ktoś może mi wyjaśnić, dlaczego mój div nigdy nie zostanie wyróżniony? Klasa jest dodawana, przetwarzanie odbywa się, a następnie klasa jest usuwana; interfejs użytkownika nie jest aktualizowany pomiędzy, a użytkownik nigdy nie widzi czerwonego tła.

Wiem, że JS jest jednowątkowe, ale zawsze zakładałem, że renderowanie przeglądarki będzie działało synchronicznie w czasie, gdy DOM jest aktualizowany. Czy moje założenie jest nieprawidłowe?

Co ważniejsze, jaki jest zalecany sposób osiągnięcia tego efektu? Czy konieczne jest użycie funkcji setTimeout, aby spowolnić przetwarzanie asynchroniczne i pracować z funkcją oddzwaniania? Musi istnieć lepszy sposób, ponieważ naprawdę nie potrzebuję tutaj zachowania asynchronicznego; Chcę tylko odświeżyć interfejs.

EDIT:

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

(uwaga, może być konieczne, aby dostosować liczbę iteracji pętli, aby dać Ci rozsądny termin na własnym komputerze)

+1

Wierzę, że takie zmiany spowodują przejście bezpośrednio do DOM, ale przeglądarka będzie czekała, aż wykonywanie JavaScriptu będzie "bezczynne" przed wykonaniem jakiegokolwiek odświeżenia. – pimvdb

+0

"Czy niektóre długotrwałe przetwarzanie" ma jakieś asynchroniczne wywołanie? (ajax, settimeout itp.) –

+0

Nie mogę naprawdę zobaczyć, gdzie twój kod idzie nie tak, ponieważ wydaje się dobrze z tego, co napisałeś. Może jest jakiś "! Important" w twoim zewnętrznym pliku css. Czy jesteś pewien, że klasa została dodana (czy sprawdziłeś ją za pomocą firebuga) i czy proces trwa tak długo? może zdarza się zbyt szybko –

Odpowiedz

0

Force a redraw. Alternatywnie, użyj Soren's code, ponieważ przetwarzanie w wątku UI brzmi źle ...

+0

Jednak wtyczki jQuery wydają się być w tej chwili niedostępne. Dodano alternatywę. Możliwe, że nie musisz odraczać 'n.parentNode.removeChild (n)'; jeśli tak, możesz edytować moją odpowiedź. –

+0

Próbowałem tego na moim jsFiddle bez powodzenia. Oczywiście nie używam go właściwie. Czy możesz dodać do Fiddle? – njr101

+0

Przepraszam, ale skrzypce nie działa. Klasa zostanie zastosowana, a następnie skrypt zostanie przerwany z błędem skryptu. Komunikat "Zakończono" nigdy nie jest wyświetlany. – njr101

1

Nie polecam zamrażać przeglądarki dla skryptu wagi. W tym przypadku wolę zmienić kod dla niezamarzającej przeglądarki i DOM. Jeśli w twoim kodzie jest jakaś pętla, możesz wywołać funkcję z opóźnieniem (używając setInterval) i zapisać gdzieś zmienną statusu. Na przykład:

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

Może być:

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

lub coś podobnego i bardziej zoptymalizowane. Twój kod będzie nieco bardziej złożony i wolniejszy. ale zapobiegasz zawieszaniu się przeglądarki i możesz utworzyć zaawansowany pasek stanu wstępnego ładowania.

+0

Dzięki, to właśnie miałem na myśli, gdy wspomniałem o 'setTimeout'. Rozumiem, że mogę to zrobić, ale miałem nadzieję uniknąć dodatkowej złożoności połączeń asynchronicznych. To otwiera całą klasę innych problemów, w których stan jest nieokreślony, gdy występuje asynchroniczne wywołanie zwrotne. – njr101

+0

Wiem. Możesz zobaczyć także webmastera http://www.html5rocks.com/en/tutorials/workers/basics/ –

+0

Dzięki za link. Interesujące rzeczy, ale niestety nie pomogą mi, ponieważ potrzebuję obsługi wielu przeglądarek. – njr101

1

Prawdopodobnie należy przetworzyć przetwarzanie w oddzielnym wydarzeniu, takim jak to;

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

ten sposób przeglądarka powinna przerysować ekran z processing, natomiast krok długo działa ruszy jako imprezy indywidualne, i usunąć ten komunikat po zakończeniu przetwarzania.

+0

Dzięki, to jest to, co podejrzewałem. Naprawdę miałem nadzieję, że istnieje sposób na uniknięcie tej siły, którą przeglądarka ponownie renderuje po zmianach w DOM. – njr101

+0

ostrzeżenie - uważam, że zaczyna działać po 5ms timeout - cokolwiek krócej, a chrom wciąż czeka. – user2486570

+0

@ user2486570 - Wątpię - JS to pętla zdarzeń z jednym gwintem - więc ma tylko te warunki wyścigu, które sam tworzysz - jeśli widzisz problem 5ms, to prawdopodobnie dlatego, że masz coś innego, co działa z timerem lub DOM gotowy do wystąpienia konfliktu z innym kodem. – Soren

0

Przeglądarka ma dość wolnego czasu na odświeżanie i czas ponownego naświetlania. Różne silniki wykazują różne zachowania. Aby uzyskać dalsze informacje, zobacz When does reflow happen in a DOM environment?. W twoim przykładzie przeglądarka widzi, że dodajesz klasę i usuwasz ją bezpośrednio po tym, więc może nie zrobić nic widocznego.

Aby tego uniknąć, można zastosować metodę "odświeżania-hacka" lub uczynić swoje obliczenia asynchronicznymi z WebWorkers lub setTimeout lub czymś podobnym.

Powiązane problemy