JavaScript'in tüm modern tarayıcı uygulamalarında tek iş parçacıklı olduğu biliniyor, ancak bu herhangi bir standartta belirtilmiş mi yoksa sadece geleneğe göre mi? JavaScript'in her zaman tek iş parçacıklı olduğunu varsaymak tamamen güvenli mi?
JavaScript'in tüm modern tarayıcı uygulamalarında tek iş parçacıklı olduğu biliniyor, ancak bu herhangi bir standartta belirtilmiş mi yoksa sadece geleneğe göre mi? JavaScript'in her zaman tek iş parçacıklı olduğunu varsaymak tamamen güvenli mi?
Yanıtlar:
Bu iyi bir soru. “Evet” demek istiyorum. Yapamam.
JavaScript'in genellikle komut dosyalarına (*) görünür tek bir yürütme iş parçacığı olduğu düşünülür;
(*: tarayıcıların JS motorlarını tek bir OS iş parçacığı kullanarak gerçekten uygulayıp uygulamadığı ya da diğer sınırlı yürütme iş parçacıklarının WebWorkers tarafından tanıtılıp tanıtılmadığı sorusunu yok saymak.)
Ancak, gerçekte bu sinsi kötü yollarla tam olarak doğru değildir .
En yaygın durum acil olaylardır. Kodunuz bunlara neden olacak bir şey yaptığında tarayıcılar bunları tetikler:
var l= document.getElementById('log');
var i= document.getElementById('inp');
i.onblur= function() {
l.value+= 'blur\n';
};
setTimeout(function() {
l.value+= 'log in\n';
l.focus();
l.value+= 'log out\n';
}, 100);
i.focus();
<textarea id="log" rows="20" cols="40"></textarea>
<input id="inp">
log in, blur, log out
IE hariç tüm sonuçlar . Bu olaylar yalnızca focus()
doğrudan aradığınız için değil , aradığınız alert()
veya bir açılır pencere veya odağı hareket ettiren başka bir şey açtığınız için de gerçekleşebilir.
Bu aynı zamanda başka olaylarla da sonuçlanabilir. Örneğin, bir i.onchange
dinleyici ekleyin ve focus()
çağrı odaklanmadan önce girdiye bir şey yazın ve günlük sırası, log in, change, blur, log out
Opera ve onun log in, blur, log out, change
bulunduğu IE (daha az açıklayıcı) hariç log in, change, log out, blur
.
Benzer şekilde click()
, bunu sağlayan bir öğeyi çağırmak , onclick
tüm tarayıcılarda işleyiciyi hemen çağırır (en azından bu tutarlıdır!).
(Ben direkt kullanarak değilim on...
burada olay işleyicisi özelliklerini ancak aynı ile olur addEventListener
ve attachEvent
.)
Ayrıca , provoke etmek için hiçbir şey yapmamış olmanıza rağmen, kodunuz işlenirken olayların tetiklenebileceği bir dizi koşul vardır . Bir örnek:
var l= document.getElementById('log');
document.getElementById('act').onclick= function() {
l.value+= 'alert in\n';
alert('alert!');
l.value+= 'alert out\n';
};
window.onresize= function() {
l.value+= 'resize\n';
};
<textarea id="log" rows="20" cols="40"></textarea>
<button id="act">alert</button>
Vur alert
ve kalıcı bir diyalog kutusu alacaksın. Bu diyaloğu kapatana kadar başka senaryo çalışmaz, değil mi? Hayır! Ana pencereyi yeniden boyutlandırdığınızda alert in, resize, alert out
metin alanına girersiniz.
Kalıcı bir iletişim kutusu varken bir pencereyi yeniden boyutlandırmanın imkansız olduğunu düşünebilirsiniz, ancak öyle değil: Linux'ta pencereyi istediğiniz kadar yeniden boyutlandırabilirsiniz; Windows'da o kadar kolay değildir, ancak ekran çözünürlüğünü pencerenin sığmadığı bir büyükten daha küçük bir düzeye değiştirerek yeniden boyutlandırılmasına neden olabilirsiniz.
Kullanıcı, tarayıcı ile aktif bir etkileşime sahip olmadığında, komut dosyası işlendiği için tetiklenebilecek yalnızca resize
(ve muhtemelen birkaç tane daha gibi scroll
) düşünebilirsiniz . Ve tek pencereler için haklı olabilirsiniz. Ancak, çapraz pencere komut dosyası oluşturduğunuzda bunların hepsi çöpe gider. Herhangi biri meşgulken tüm pencereleri / sekmeleri / çerçeveleri engelleyen Safari dışındaki tüm tarayıcılar için, ayrı bir yürütme iş parçacığında çalışan ve ilgili olay işleyicilerinin çalışmasına neden olan başka bir belgenin kodundan bir belgeyle etkileşimde bulunabilirsiniz. ateş.
Komut dosyası işlenirken oluşturulabilecek olayların oluşturulabileceği yerler yükseltilebilir:
Modal pop-up (zaman alert
, confirm
, prompt
), açık tüm tarayıcılarda ama Opera'da vardır;
sırasında showModalDialog
bunu destekleyen tarayıcılar üzerinde;
"Bu sayfadaki bir komut dosyası meşgul olabilir ..." iletişim kutusu, komut dosyasının çalışmaya devam etmesine izin verseniz bile, yeniden boyutlandırma ve bulanıklaştırma gibi olayların tetiklenmesine izin verir ve komut dosyası bir ortalamanın ortasındayken bile işlenir Meşgul döngü, Opera hariç.
Bir süre önce benim için, IE'de Sun Java Eklentisi ile, bir uygulamadaki herhangi bir yöntemi çağırmak olayların tetiklenmesine ve komut dosyasının tekrar girilmesine izin verebilir. Bu her zaman zamanlamaya duyarlı bir hataydı ve Sun'ın o zamandan beri düzeltmesi mümkündür (kesinlikle umarım).
muhtemelen daha fazla. Bunu test ettiğimden beri bir süre geçti ve tarayıcılar o zamandan beri karmaşıklık kazandı.
Özetle, JavaScript çoğu kullanıcı için, çoğu zaman katı bir olaya dayalı tek bir yürütme iş parçacığına sahip gibi görünür. Gerçekte böyle bir şey yok. Bunun ne kadarının bir hata olduğu ve ne kadar kasıtlı bir tasarım olduğu açık değildir, ancak karmaşık uygulamalar, özellikle çapraz pencere / çerçeve komut dosyası uygulamaları yazıyorsanız, sizi ısırması için her şans vardır - ve aralıklı olarak, hata ayıklaması zor yollar.
En kötüsü en kötüsüne gelirse, tüm olay yanıtlarını indirerek eşzamanlılık sorunlarını çözebilirsiniz. Bir olay geldiğinde, onu bir kuyruğa bırakın ve daha sonra sırayla, bir setInterval
işlevle kuyrukla ilgilenin . Karmaşık uygulamalar tarafından kullanılmak istediğiniz bir çerçeve yazıyorsanız, bunu yapmak iyi bir hareket olabilir. postMessage
ayrıca gelecekte çapraz belge komut dosyası yazma ağrısını hafifletecektir.
blur
sonra IE tetiklenir .
Evet diyorum - çünkü bir tarayıcının javascript motoru senkronize olmayan bir şekilde çalıştırmak için neredeyse tüm mevcut (en azından tüm önemsiz olmayan) javascript kodu kırılır.
HTML5'in , temel Javascript'e çoklu iş parçacığını tanıtan Web Workers'ı (çoklu iş parçacığı javascript kodu için açık, standart bir API) zaten belirttiği gerçeğini de ekleyin .
( Diğerleri yorum yazanlara Not: rağmen setTimeout/setInterval
tek tek - - onlar hala tek bir zaman çizgisi boyunca yürütülür, HTTP isteği olayları (XHR) ve UI olayları (tıklama, odak, vs.) çok threadedness bir ham izlenim sağlamak Onload bir süre - bu nedenle, yürütme sırasını önceden bilmesek bile, bir olay işleyicinin, zamanlanmış işlevin veya XHR geri aramasının yürütülmesi sırasında dış koşulların değişmesi konusunda endişelenmenize gerek yoktur.)
Söyleyebilirm şartname engellemez birilerini bir motor yaratarak o çalışır üzerinde javascript birden çok iş parçacığı paylaşılan nesne durumuna erişmek için eşitleme gerçekleştirmek için kod gerektiren.
Tek iş parçacıklı, engellemeyen paradigmanın , ui'nin asla engellememesi gereken tarayıcılarda javascript çalıştırma ihtiyacından kaynaklandığını düşünüyorum .
Nodejs tarayıcıların yaklaşımını izledi .
Ancak Rhino motoru, farklı iş parçacıklarında js kodu çalıştırmayı destekler . İcralar bağlamı paylaşamaz, ancak kapsamı paylaşabilirler. Bu özel durum için belgeler şunları belirtir:
... "Rhino, JavaScript nesnelerinin özelliklerine erişimin iş parçacıkları arasında atomik olduğunu garanti eder, ancak aynı kapsamda aynı anda yürütülen komut dosyaları için daha fazla garanti vermez. İki komut dosyası aynı kapsamı aynı anda kullanırsa, komut dosyaları paylaşılan değişkenlere erişimi koordine etmekten sorumludur . "
Rhino belgelerini okuduğumdan, birisinin yeni javascript ipliklerini de üreten bir javascript api yazmasının mümkün olabileceği sonucuna varıyorum, ancak api gergiye özgü olacaktı (örneğin, düğüm yalnızca yeni bir süreç oluşturabilir).
Javascript içinde birden fazla iş parçacığı destekleyen bir motor için bile çoklu iş parçacığı veya engelleme dikkate almaz komut dosyaları ile uyumluluk olması gerektiğini düşünüyorum.
Concearning tarayıcıları ve nodejs şekilde ben öyle bakın:
Yani, tarayıcılar ve nodejs (ve muhtemelen diğer birçok motorda) javascript çok iş parçacıklı değildir, ancak motorların kendisidir .
Web çalışanlarının varlığı, birisinin javascript'te ayrı bir iş parçacığında çalışacak kod oluşturabileceği anlamında, javascript'in çok iş parçacıklı olabileceğini daha da haklı çıkarır.
Ancak, web çalışanları , yürütme bağlamını paylaşabilen geleneksel iş parçacıklarının sorunlarını azaltmazlar. Yukarıdaki kural 2 ve 3 hala geçerlidir , ancak bu kez iş parçacığı kodu javascript kullanıcı (js kodu yazar) tarafından oluşturulur.
Dikkate alınması gereken tek şey , verimlilik ( eşzamanlılık değil ) açısından ortaya çıkan ipliklerin sayısıdır . Aşağıya bakınız:
İşçi arabirimi gerçek OS düzeyindeki iş parçacıklarını ortaya çıkarır ve dikkatli programcılar, eşzamanlılığın dikkatli değilseniz kodunuzda "ilginç" etkilere neden olabileceğinden endişe edebilir.
Bununla birlikte, web çalışanları diğer iş parçacıkları ile iletişim noktalarını dikkatlice kontrol ettikleri için eşzamanlılık sorunlarına neden olmak çok zordur . Güvenli olmayan bileşenlere veya DOM'ye erişim yok. Ve belirli verileri serileştirilmiş nesnelerden bir iş parçacığına girip çıkarmanız gerekir. Bu nedenle, kodunuzda sorunlara neden olmak için gerçekten çok çalışmalısınız.
Teorinin yanı sıra, her zaman kabul edilen cevapta tanımlanan olası köşe vakaları ve hatalar hakkında hazırlıklı olun
JavaScript / ECMAScript bir ana bilgisayar ortamında yaşamak için tasarlanmıştır. Yani, ana bilgisayar ortamı belirli bir komut dosyasını ayrıştırmaya ve yürütmeye karar vermedikçe ve JavaScript'in gerçekten kullanışlı olmasına izin veren ortam nesneleri (tarayıcılardaki DOM gibi) sağlamadığı sürece JavaScript aslında hiçbir şey yapmaz .
Belirli bir işlev veya komut dosyası satır satır yürütmek ve JavaScript için garanti düşünüyorum. Ancak, bir ana bilgisayar ortamı aynı anda birden çok komut dosyası yürütebilir. Veya, bir ana bilgisayar ortamı her zaman çoklu iş parçacığı sağlayan bir nesne sağlayabilir. setTimeout
ve setInterval
bir eşzamanlılık (tam eşzamanlılık olmasa bile) bir yol sağlayan bir ana bilgisayar ortamına örnekler veya en azından sözde örneklerdir.
Hayır.
Buradaki kalabalığa karşı çıkıyorum, ama bana katlan. Tek bir JS betiğinin etkili bir şekilde tek iş parçacıklı olması amaçlanmıştır , ancak bu farklı yorumlanamayacağı anlamına gelmez.
Diyelim ki aşağıdaki koda sahipsiniz ...
var list = [];
for (var i = 0; i < 10000; i++) {
list[i] = i * i;
}
Bu, döngünün sonunda, listenin dizin karesi olan 10000 girişe sahip olması beklenirken yazılır, ancak VM, döngünün her yinelemesinin diğerini etkilemediğini fark edebilir ve iki iş parçacığı kullanarak yeniden yorumlayabilir.
İlk iş parçacığı
for (var i = 0; i < 5000; i++) {
list[i] = i * i;
}
İkinci iş parçacığı
for (var i = 5000; i < 10000; i++) {
list[i] = i * i;
}
Burada basitleştiriyorum, çünkü JS dizileri aptal bellek parçalarından daha karmaşıktır, ancak bu iki komut dosyası diziye iş parçacığı güvenli bir şekilde girişler ekleyebiliyorsa, o zaman her ikisi de yürütülürken tek iş parçacıklı sürümle aynı sonucu verir.
Bunun gibi paralelleştirilebilir kod algılayan herhangi bir VM'nin farkında olmasam da, gelecekte bazı durumlarda daha fazla hız sunabileceğinden, JIT VM'leri için gelecekte ortaya çıkabileceği düşünülmektedir.
Bu kavramı daha ileri götürerek, VM'nin çok iş parçacıklı koda nasıl dönüştürüleceğini bilmesini sağlamak için kodun açıklanabilmesi mümkündür.
// like "use strict" this enables certain features on compatible VMs.
"use parallel";
var list = [];
// This string, which has no effect on incompatible VMs, enables threading on
// this loop.
"parallel for";
for (var i = 0; i < 10000; i++) {
list[i] = i * i;
}
Web Çalışanları Javascript'e geldiğinden beri, bu ... çirkin sistemin ortaya çıkması pek olası değildir, ancak Javascript'in geleneklere göre tek iş parçacıklı olduğunu söylemenin güvenli olduğunu düşünüyorum.
Peki, Chrome çok işlemlidir ve her sürecin kendi Javascript koduyla uğraştığını düşünüyorum, ancak kodun bildiği kadarıyla "tek iş parçacıklı" dır.
Javascript'te çoklu iş parçacığı için herhangi bir destek yoktur, en azından açıkça değil, bu yüzden bir fark yaratmaz.
Küçük bir değişiklikle @ bobince örneğini denedim:
<html>
<head>
<title>Test</title>
</head>
<body>
<textarea id="log" rows="20" cols="40"></textarea>
<br />
<button id="act">Run</button>
<script type="text/javascript">
let l= document.getElementById('log');
let b = document.getElementById('act');
let s = 0;
b.addEventListener('click', function() {
l.value += 'click begin\n';
s = 10;
let s2 = s;
alert('alert!');
s = s + s2;
l.value += 'click end\n';
l.value += `result = ${s}, should be ${s2 + s2}\n`;
l.value += '----------\n';
});
window.addEventListener('resize', function() {
if (s === 10) {
s = 5;
}
l.value+= 'resize\n';
});
</script>
</body>
</html>
Bu nedenle, Çalıştır'a bastığınızda, uyarı açılır penceresini kapatıp "tek bir iş parçacığı" yaptığınızda şöyle bir şey görmelisiniz:
click begin
click end
result = 20, should be 20
Ancak bunu Windows'ta Opera veya Firefox'ta kararlı hale getirmeye ve ekranda uyarı açılır penceresiyle pencereyi simge durumuna küçültme / büyütmeye çalışırsanız, şöyle bir şey olacaktır:
click begin
resize
click end
result = 15, should be 20
Bu "multithreading" olduğunu söylemek istemiyorum, ama bazı kod parçası ben bunu beklemiyorsun benimle yanlış bir zamanda yürüttü ve şimdi bozuk bir durum var. Ve bu davranışı bilmek daha iyi.
İki setTimeout işlevini iç içe yerleştirmeye çalışın ve çok iş parçacıklı davranırlar (yani dış zamanlayıcı, işlevini yerine getirmeden önce iç işlevin tamamlanmasını beklemez).
setTimeout(function(){setTimeout(function(){console.log('i herd you liek async')}, 0); alert('yo dawg!')}, 0)
(kayıt için yo dostum HER ZAMAN, ilk önce konsol günlük çıkışını gelmelidir)