JavaScript jest znany jako jednowątkowy we wszystkich nowoczesnych implementacjach przeglądarki, ale czy jest to określone w jakimkolwiek standardzie, czy to tylko z tradycji? Czy można całkowicie założyć, że JavaScript jest zawsze jednowątkowy?
javascript
concurrency
Egor Pavlikhin
źródło
źródło
Odpowiedzi:
To dobre pytanie. Chciałbym powiedzieć „tak”. Nie mogę
Zazwyczaj uważa się, że JavaScript ma pojedynczy wątek wykonania widoczny dla skryptów (*), dzięki czemu po wprowadzeniu skryptu wbudowanego, detektora zdarzeń lub przekroczenia limitu czasu masz pełną kontrolę do momentu powrotu z końca bloku lub funkcji.
(*: ignorując pytanie, czy przeglądarki rzeczywiście implementują swoje silniki JS za pomocą jednego wątku systemu operacyjnego, czy też WebWorkers wprowadza inne ograniczone wątki wykonania).
Jednak w rzeczywistości nie jest to do końca prawdą , podstępnie i podstępnie.
Najczęstszym przypadkiem są zdarzenia natychmiastowe. Przeglądarki uruchomią je natychmiast, gdy Twój kod zrobi coś, co je spowoduje:
Wyniki we
log in, blur, log out
wszystkich oprócz IE. Te wydarzenia nie tylko odpalają, ponieważ dzwoniłeśfocus()
bezpośrednio, ale mogą się zdarzyć, ponieważ zadzwoniłeśalert()
lub otworzyłeś wyskakujące okienko lub cokolwiek innego, co porusza fokus.Może to również skutkować innymi zdarzeniami. Na przykład dodaj
i.onchange
nasłuchiwanie i wpisz coś w danych wejściowych, zanimfocus()
wywołanie zostanie nieuzbrojone, a kolejność dziennika jestlog in, change, blur, log out
inna niż w Operze, gdzie jest,log in, blur, log out, change
i IE, gdzie jest (nawet mniej zrozumiale)log in, change, log out, blur
.Podobnie wywołanie
click()
elementu, który go zapewnia, wywołujeonclick
moduł obsługi natychmiast we wszystkich przeglądarkach (przynajmniej jest to zgodne!).(Korzystam
on...
tutaj z właściwości bezpośredniego modułu obsługi zdarzeń, ale to samo dzieje się zaddEventListener
iattachEvent
).Istnieje również szereg okoliczności, w których zdarzenia mogą być uruchamiane, gdy Twój kod jest włączony, mimo że nie zrobiłeś nic, aby go sprowokować. Przykład:
Hit,
alert
a otrzymasz modalne okno dialogowe. Koniec wykonywania skryptu, dopóki nie odrzucisz tego dialogu, tak? Nie. Zmień rozmiar głównego okna, a otrzymaszalert in, resize, alert out
pole tekstowe.Możesz myśleć, że zmiana rozmiaru okna jest niemożliwa, gdy modalne okno dialogowe jest uruchomione, ale nie jest tak: w Linuksie możesz zmieniać rozmiar okna tak, jak chcesz; w systemie Windows nie jest to takie łatwe, ale możesz to zrobić, zmieniając rozdzielczość ekranu z większej na mniejszą, w której okno nie pasuje, powodując zmianę rozmiaru.
Możesz pomyśleć, cóż, to tylko
resize
(i prawdopodobnie kilka innychscroll
) może się uruchomić, gdy użytkownik nie ma aktywnej interakcji z przeglądarką, ponieważ skrypt jest wątkowy. A w przypadku pojedynczych okien możesz mieć rację. Ale wszystko to idzie w garść, gdy tylko zaczniesz pisać skrypty w różnych oknach. W przypadku wszystkich przeglądarek innych niż Safari, które blokują wszystkie okna / karty / ramki, gdy jedna z nich jest zajęta, możesz wchodzić w interakcje z dokumentem z kodu innego dokumentu, działając w osobnym wątku wykonania i powodując, że wszystkie powiązane zdarzenia obsługują zdarzenia ogień.Miejsca, w których zdarzenia mogą być generowane, mogą być wywoływane, gdy skrypt jest wciąż podzielony na wątki:
gdy modalne pop-upy (
alert
,confirm
,prompt
) są otwarte, ale we wszystkich przeglądarkach Opera;podczas
showModalDialog
w przeglądarkach, które go obsługują;okno dialogowe „Skrypt na tej stronie może być zajęty ...”, nawet jeśli zdecydujesz się na kontynuowanie działania skryptu, pozwala na uruchamianie zdarzeń takich jak zmiana rozmiaru i rozmycie, nawet gdy skrypt jest w środku zajęty, z wyjątkiem Opery.
jakiś czas temu dla mnie, w IE z wtyczką Sun Java, wywołanie dowolnej metody w aplecie może pozwolić na uruchomienie zdarzeń i ponowne wprowadzenie skryptu. To zawsze był błąd zależny od czasu i możliwe, że Sun go naprawił (mam nadzieję, że tak).
prawdopodobnie więcej. Minęło trochę czasu, odkąd to przetestowałem, a przeglądarki stały się bardziej złożone.
Podsumowując, większość użytkowników JavaScript wydaje się mieć pojedynczy wątek wykonania oparty na zdarzeniach. W rzeczywistości nie ma czegoś takiego. Nie jest jasne, ile z tego jest po prostu błędem, a ile przemyślanego projektu, ale jeśli piszesz złożone aplikacje, zwłaszcza takie, które wykorzystują skrypty okien / ramek, istnieje szansa, że cię ugryzie - i sporadycznie, trudne do debugowania sposoby.
Jeśli najgorsze dojdzie do najgorszego, możesz rozwiązać problemy z współbieżnością, pośrednicząc we wszystkich reakcjach na zdarzenia. Kiedy nadejdzie zdarzenie, upuść je w kolejce i załatw kolejkę w kolejności później, w
setInterval
funkcji. Jeśli piszesz platformę, której zamierzasz używać w złożonych aplikacjach, zrobienie tego może być dobrym posunięciem.postMessage
Mam nadzieję, że w przyszłości złagodzi ból związany ze skryptami między dokumentami.źródło
blur
gdy kod zwróci kontrolę nad przeglądarką.Powiedziałbym tak - ponieważ praktycznie cały istniejący (przynajmniej cały nietrywialny) kod javascript zepsułby się, gdyby silnik javascript przeglądarki działał asynchronicznie.
Dodaj do tego fakt, że HTML5 już określa pracowników sieci (jawny, znormalizowany API dla wielowątkowego kodu javascript) wprowadzający wielowątkowość do podstawowego Javascript byłby w większości bezcelowy.
( Uwaga dla innych komentujących: chociaż
setTimeout/setInterval
zdarzenia obciążenia żądania HTTP (XHR) i zdarzenia interfejsu użytkownika (kliknięcie, fokus itp.) Zapewniają ogólne wrażenie wielowątkowości - wszystkie są nadal wykonywane wzdłuż jednej osi czasu - jeden na czas - więc nawet jeśli nie znamy wcześniej ich kolejności wykonania, nie musisz się martwić zmianami warunków zewnętrznych podczas wykonywania procedury obsługi zdarzenia, funkcji czasowej lub wywołania zwrotnego XHR.)źródło
Tak, chociaż nadal możesz cierpieć na niektóre problemy z jednoczesnym programowaniem (głównie warunki wyścigu) podczas korzystania z asynchronicznych interfejsów API, takich jak wywołania zwrotne setInterval i xmlhttp.
źródło
Tak, chociaż Internet Explorer 9 skompiluje JavaScript w osobnym wątku w ramach przygotowań do wykonania w głównym wątku. Nie zmienia to jednak niczego dla ciebie jako programisty.
źródło
Powiedziałbym, że specyfikacja nie uniemożliwia komuś stworzenia silnika, który uruchamia javascript w wielu wątkach , wymagając od kodu wykonania synchronizacji w celu uzyskania dostępu do stanu obiektu współużytkowanego.
Myślę, że jednowątkowy paradygmat nieblokujący wynikał z potrzeby uruchamiania javascript w przeglądarkach, w których interfejs użytkownika nigdy nie powinien blokować.
Nodejs podążył za podejściem przeglądarki .
Silnik Rhino obsługuje jednak uruchamianie kodu js w różnych wątkach . Wykonania nie mogą współdzielić kontekstu, ale mogą współdzielić zakres. W tym konkretnym przypadku dokumentacja stanowi:
Z lektury dokumentacji Rhino wywnioskowałem, że ktoś może napisać API javascript, które również spawnuje nowe wątki javascript, ale API byłoby specyficzne dla nosorożców (np. Węzeł może tylko spawnować nowy proces).
Wyobrażam sobie, że nawet w przypadku silnika obsługującego wiele wątków w javascript powinna istnieć kompatybilność ze skryptami, które nie uwzględniają wielowątkowości lub blokowania.
Ukrywanie przeglądarek i nodejs tak, jak widzę to:
Tak więc w przypadku przeglądarek i nodejs (i prawdopodobnie wielu innych silników) javascript nie jest wielowątkowy, ale same silniki są .
Aktualizacja o pracownikach sieci:
Obecność pracowników sieci uzasadnia ponadto, że javascript może być wielowątkowy, w tym sensie, że ktoś może utworzyć kod w javascript, który będzie działał w osobnym wątku.
Jednak: pracownicy sieci nie rozwiązują problemów tradycyjnych wątków, które mogą współdzielić kontekst wykonania. Nadal obowiązują powyższe reguły 2 i 3 , ale tym razem kod wątkowy jest tworzony przez użytkownika (program piszący js) w javascript.
Jedyną rzeczą do rozważenia jest liczba spawnowanych wątków z punktu widzenia wydajności (a nie współbieżności ). Patrz poniżej:
O bezpieczeństwie wątków :
PS
Oprócz teorii zawsze przygotuj się na możliwe przypadki narożne i błędy opisane w zaakceptowanej odpowiedzi
źródło
JavaScript / ECMAScript został zaprojektowany do pracy w środowisku hosta. Oznacza to, że JavaScript w rzeczywistości nic nie robi chyba że środowisko hosta zdecyduje się przeanalizować i wykonać dany skrypt oraz zapewnić obiekty środowiska, które pozwolą JavaScriptowi faktycznie się przydać (takie jak DOM w przeglądarkach).
Myślę, że dana funkcja lub blok skryptu będzie wykonywany wiersz po wierszu i jest to gwarantowane w JavaScript. Być może jednak środowisko hosta może wykonywać wiele skryptów jednocześnie. Lub środowisko hosta zawsze może zapewniać obiekt zapewniający wielowątkowość.
setTimeout
isetInterval
są przykładami lub przynajmniej pseudo-przykładami środowiska hosta zapewniającymi sposób wykonywania współbieżności (nawet jeśli nie jest to dokładnie współbieżność).źródło
W rzeczywistości okno nadrzędne może komunikować się z oknami lub ramkami potomnymi lub rodzeństwem, które mają uruchomione własne wątki wykonawcze.
źródło
@ Bobince zapewnia naprawdę nieprzejrzystą odpowiedź.
Odrzucając odpowiedź Marla Örlygssona, JavaScript jest zawsze jednowątkowy z tego prostego faktu: wszystko w Javascript jest wykonywane na jednej linii czasu.
Jest to ścisła definicja jednowątkowego języka programowania.
źródło
Nie.
Idę tutaj przeciwko tłumowi, ale bądźcie ze mną. Pojedynczy skrypt JS ma być efektywnie jednowątkowy, ale to nie znaczy, że nie można go interpretować inaczej.
Powiedzmy, że masz następujący kod ...
Jest to napisane z oczekiwaniem, że do końca pętli lista musi zawierać 10000 pozycji, które są kwadratem indeksu, ale maszyna wirtualna może zauważyć, że każda iteracja pętli nie wpływa na drugą, i ponownie zinterpretować za pomocą dwóch wątków.
Pierwszy wątek
Drugi wątek
Upraszczam tutaj, ponieważ tablice JS są bardziej skomplikowane niż głupie fragmenty pamięci, ale jeśli te dwa skrypty są w stanie dodać wpisy do tablicy w sposób bezpieczny dla wątków, to zanim oba zostaną wykonane, będzie miała taki sam wynik jak wersja jednowątkowa.
Chociaż nie jestem świadomy, że jakakolwiek maszyna wirtualna wykrywa taki równoległy kod, wydaje się prawdopodobne, że mogłaby powstać w przyszłości dla maszyn wirtualnych JIT, ponieważ w niektórych sytuacjach mogłaby oferować większą prędkość.
Kontynuując tę koncepcję, możliwe jest, że kod zostanie opatrzony adnotacjami, aby maszyna wirtualna wiedziała, co przekonwertować na kod wielowątkowy.
Ponieważ pracownicy sieci przychodzą do Javascript, jest mało prawdopodobne, aby ten ... brzydszy system kiedykolwiek powstał, ale myślę, że bezpiecznie jest powiedzieć, że Javascript jest tradycją jednowątkową.
źródło
Cóż, Chrome jest wieloprocesowy i myślę, że każdy proces zajmuje się własnym kodem JavaScript, ale o ile kod wie, jest „jednowątkowy”.
W JavaScript nie ma żadnej obsługi wielowątkowości, przynajmniej nie jawnie, więc nie robi to różnicy.
źródło
Wypróbowałem przykład @ bobince z niewielkimi modyfikacjami:
Tak więc, kiedy naciśniesz Uruchom, zamknij okienko ostrzeżenia i wykonasz „pojedynczy wątek”, powinieneś zobaczyć coś takiego:
Ale jeśli spróbujesz uruchomić to w stabilnej przeglądarce Opera lub Firefox w systemie Windows i zminimalizować / zmaksymalizować okno z wyskakującym okienkiem na ekranie, pojawi się coś takiego:
Nie chcę powiedzieć, że jest to „wielowątkowość”, ale jakiś fragment kodu został wykonany w niewłaściwym czasie, a ja nie spodziewałem się tego, a teraz mam uszkodzony stan. I lepiej wiedzieć o tym zachowaniu.
źródło
Spróbuj zagnieździć w sobie dwie funkcje setTimeout, które będą zachowywać się wielowątkowo (tzn. Zewnętrzny zegar nie będzie czekał na zakończenie wewnętrznego przed wykonaniem swojej funkcji).
źródło
setTimeout(function(){setTimeout(function(){console.log('i herd you liek async')}, 0); alert('yo dawg!')}, 0)
(dla przypomnienia, twój ZAWSZE powinien ZAWSZE pojawiać się najpierw, a następnie logować się do konsoli)