Bu iki en yüksek puanlı cevap yanlış. Eşzamanlılık modeli ve olay döngüsündeki MDN açıklamasına göz atın ve neler olduğunu netleştirin (MDN kaynağı gerçek bir mücevherdir). Ve sadece setTimeout
bu küçük sorunu "çözmenin" yanı sıra kodunuza beklenmedik sorunlar eklemek olabilir.
Ne var aslında burada devam değil mi "tarayıcı tam olarak hazır olmayabilir henüz eşzamanlılık, çünkü" dayalı falan "her satırın sıranın arkasına eklenen alır bir olaydır".
Jsfiddle Dvk tarafından sağlanan gerçekten bir sorun göstermektedir, ancak bunun için onun açıklaması doğru değil.
Kodunda olan şey click
, #do
düğmedeki olaya ilk önce bir olay işleyici ekliyor olmasıdır .
Ardından, düğmeyi gerçekten tıklattığınızda, message
olay işleyici işlevine başvuruda bulunan ve öğesine eklenir message queue
. Ne zaman event loop
bu mesajı ulaştığında, bir oluşturur frame
jsfiddle tıklama olay işleyicisi işlev çağrısı ile, yığının üzerine.
Ve işte ilginç olan yer burası. Javascript'i asenkron olarak düşünmeye o kadar alışkınız ki, bu küçük gerçeği göz ardı etmeye meyilliyiz: Herhangi bir kare, bir sonraki kare yürütülmeden önce tam olarak yürütülmelidir . Eşzamanlılık yok, insanlar.
Ne anlama geliyor? Bu, ileti kuyruğundan bir işlev her çağrıldığında, oluşturduğu yığın boşaltılana kadar kuyruğu bloke ettiği anlamına gelir. Veya daha genel terimlerle, işlev dönünceye kadar engeller. Ayrıca , DOM oluşturma işlemleri, kaydırma ve başka şeyler dahil her şeyi engeller . Onaylamak istiyorsanız, kemandaki uzun çalışma işleminin süresini artırmaya çalışın (örn. Dış döngüyü 10 kez daha çalıştırın) ve çalışırken bu sayfayı kaydıramayacağınızı göreceksiniz. Yeterince uzun sürerse, tarayıcınız size işlemi yanıtlamak isteyip istemediğinizi soracaktır çünkü sayfa yanıt vermiyor. Çerçeve yürütülür ve olay döngüsü ve ileti kuyruğu bitene kadar takılı kalır.
Peki neden metnin bu yan etkisi güncellenmiyor? Eğer ederken Çünkü var DOM öğesinin değerini değişti - yapabilirsiniz console.log()
değeri hemen değiştirdikten sonra ve görüyoruz etmiştir (ki gösteriler Dvk yaptığı açıklama doğru değil neden) değiştirilmiştir - Tarayıcı tüketme yığını bekliyor ( on
geri dönmek için işleyici işlevi) ve böylece bitecek mesaj, böylece sonunda çalışma zamanı tarafından mutasyon operasyonumuza bir tepki olarak eklenen mesajı yürütmeye ve kullanıcı arayüzündeki bu mutasyonu yansıtmaya çalışabilir. .
Çünkü kodun çalışmasını bitirmesini bekliyoruz. Biz genellikle "birileri bunu getirin ve daha sonra sonuçlarla bu işlevi çağırır, teşekkürler, ve ben şimdi imma dönüş yapmak, şimdi ne olursa olsun yapmak" demedik, genellikle olay tabanlı asenkron Javascript ile yaptığımız gibi. Bir tıklama olay işleyicisi işlevi giriyoruz, bir DOM öğesini güncelliyoruz, başka bir işlev çağırıyoruz, diğer işlev uzun süre çalışıyor ve sonra geri dönüyor, daha sonra aynı DOM öğesini güncelliyoruz ve sonra ilk işlevden geri dönüyoruz, etkili bir şekilde boşalıyoruz yığını. Ve sonra tarayıcı sıradaki bir sonraki mesaja ulaşabilir, bu da dahili bir "DOM-mutasyon" tipi olayı tetikleyerek bizim tarafımızdan oluşturulan bir mesaj olabilir.
Tarayıcı kullanıcı arayüzü, şu anda yürütülen çerçeve tamamlanıncaya (işlev geri döndü) kadar kullanıcı arayüzünü güncelleyemez (veya etmemeyi seçer). Şahsen, bunun kısıtlamadan ziyade tasarımdan kaynaklandığını düşünüyorum.
O zaman neden setTimeout
işe yarıyor? Bunu yapar, çünkü uzun süren işlev çağrısını kendi çerçevesinden etkili bir şekilde kaldırır, daha sonra window
bağlamda yürütülecek şekilde zamanlar , böylece kendisi hemen geri dönebilir ve ileti sırasının diğer iletileri işlemesine izin verebilir. Ve fikir şu ki, DOM'daki metni değiştirirken bizim tarafımızdan Javascript'te tetiklenen UI "güncelleme" mesajı artık uzun süren işlev için sıralanan mesajın önünde, böylece UI güncellemesi engellenmeden önce gerçekleşiyor uzun zamandır.
A) Uzun süren işlevin çalıştığında hala her şeyi engellediğini ve b) UI güncellemesinin mesaj kuyruğunda aslında bunun ötesinde olduğunu garanti etmezsiniz. Haziran 2018 Chrome tarayıcımda, değeri 0
kemanın gösterdiği sorunu "düzeltmiyor" - 10. Aslında bununla biraz boğuldum, çünkü UI güncelleme mesajının kendisinden önce sıraya alınması gerektiği mantıklı görünüyor, çünkü uzun süren işlevi "daha sonra" çalışacak şekilde zamanlamadan önce tetikleyicisi yürütülüyor. Ama belki de V8 motorunda müdahale edebilecek bazı optimizasyonlar var, ya da benim anlayışım eksik.
Tamam, peki, kullanımdaki sorun setTimeout
nedir ve bu özel durum için daha iyi bir çözüm nedir?
Öncelikle, setTimeout
başka bir sorunu hafifletmek için böyle bir olay işleyicide kullanma sorunu, diğer kodlarla uğraşmaya eğilimlidir. İşte işimden gerçek bir örnek:
Bir meslektaş, olay döngüsü hakkında yanlış bilgilendirilmiş bir anlayışla, bazı şablon oluşturma kodunun oluşturulması setTimeout 0
için Javascript'i "işlemeye" çalıştı . Artık sormak için burada değil, ama belki de oluşturma hızını ölçmek için zamanlayıcılar eklediğini varsayabilirim (ki bu fonksiyonların geri dönüşü olacaktır) ve bu yaklaşımı kullanmanın bu fonksiyondan şaşırtıcı derecede hızlı yanıtlar vereceğini buldu.
İlk sorun açıktır; javascript işleyemezsiniz, bu yüzden gizleme eklerken burada hiçbir şey kazanmazsınız. İkincisi, artık bir şablonun oluşturulmasını, çok şablonun oluşturulmasını bekleyebilecek olası olay dinleyicileri yığınından etkili bir şekilde ayırdınız, ancak çok iyi olmasa da. Bu işlevin gerçek davranışı artık - bilmeden öyle - onu çalıştıracak ya da ona bağlı olacak herhangi bir işlev olduğu için deterministik değildi. Eğitimli tahminler yapabilirsiniz, ancak davranışını düzgün bir şekilde kodlayamazsınız.
Onun mantığına bağlı yeni bir olay işleyicisi yazarken "düzeltme" de kullanmaktı setTimeout 0
. Ancak, bu bir düzeltme değildir, anlaşılması zordur ve böyle bir koddan kaynaklanan hataların hatalarını ayıklamak eğlenceli değildir. Bazen hiçbir sorun yoktur, bazen de sürekli olarak başarısız olur ve daha sonra, platformun mevcut performansına ve o sırada neler olup bittiğine bağlı olarak, bazen aralıklı olarak çalışır ve kırılır. Bu yüzden , ne yaptığınızı ve sonuçların ne olduğunu gerçekten bilmiyorsanız, bu hack'i (kişisel olarak bir hack'tir ve hepimiz bilmeliyiz) kullanmaya karşı kişisel olarak tavsiye ederim.
Ama bunun yerine ne yapabiliriz ? Başvurulan MDN makalesinde belirtildiği gibi, işi birden çok mesaja (eğer mümkünse) ayırın, böylece sıraya alınan diğer iletiler çalışmanızla araya eklenebilir ve çalışırken yürütülebilir veya çalışabilecek bir web çalışanı kullanabilir sayfanıza paralel olarak ve hesaplamalarla işiniz bittiğinde sonuçları döndürür.
Oh, ve "Peki, asenkron işlevini yapmak için uzun süredir devam eden fonksiyona bir geri arama koyamadım mı?" Diye düşünüyorsan o zaman hayır. Geri arama, eşzamansız hale getirmez, geri aramanızı açıkça çağırmadan önce uzun süren kodu çalıştırması gerekir.