span'ın javascript innerHTML'si görev işlenmeden önce değişmiyor
Sevgili stackoverflow topluluğu,
Bu, sorunumun çok basitleştirilmiş bir versiyonu.
spanHTML’lerime bir yazdım bodyve HTML’nin JavaScript’ini spanbelirli bir görevi işlemeden önce ’nin metnini değiştirecek şekilde ayarladım .
Ancak JavaScript span, işlevi işlemeden önce metnini değiştirmemeye karar verir FOR(). Bunun yerine span, verilen görevi tamamladıktan sonra 'ın metnini değiştirir . Tamamen göz ardı edilerek, span'nin metnini değiştirme komutunun, işleme görevini yapmak için komutun bir satır yukarısına konumlandırılması.
Bu neden ve bunu nasıl düzeltebilirim? ( spanJavaScript'in görevi işlemeye başlamadan önce metninin değiştirilmesini istiyorum )
<html>
<body>
<button onclick="pressed()">press here</button>
<span id="text">still the same text</span>
<script>
function FOR(rounds)
{
var x = 0;
for(var i=0; i<rounds; i++)
{
x+=Math.sqrt(i);
}
return(x);
}
function pressed()
{
var text = document.getElementById("text");
text.innerHTML = "i have changed";
console.log(FOR(2000000000));
console.log("processing done");
}
</script>
</body>
</html>
Yanıtlar
Metin DOM'da eşzamanlı olarak değiştirilir, ancak ekrana oluşturma yalnızca "oluşturmayı güncelle" adımlarının çağrılacağı belirli bir olay döngüsü yinelemesinde (bundan sonra " boyama çerçevesi " olarak anılacaktır ) yapılacaktır.
Görevin eşzamanlı olarak engellenmesi, tarayıcının olay döngüsünün bu kısmına erişmesine izin vermez ve bu nedenle arada herhangi bir işleme gerçekleşmez.
Engelleme komut dosyanızı çağırmadan önce bir sonraki resim çerçevesinin ateşlenmesini bekleyebilirsiniz :
function FOR(rounds) {
var x = 0;
for (var i = 0; i < rounds; i++) {
x += Math.sqrt(i);
}
return (x);
}
function pressed() {
var text = document.getElementById("text");
text.innerHTML = "i have changed";
// requestAnimationFrame schedules a callback to fire before the next rendering
requestAnimationFrame( () => {
// so we still need to wait the next iteration of the event loop
setTimeout(() => {
console.log(FOR(2000000000));
console.log("processing done");
},0);
});
}
<button onclick="pressed()">press here</button>
<span id="text">still the same text</span>
Ancak en iyisi, kullanıcı arayüzünüzü hiç kilitlememek.
Yapılması gereken ağır hesaplamalarınız varsa, tarayıcınızın hesaplamaları gerçekleştirmek için başka bir iş parçacığı kullanmasına izin verecek bir Web Çalışanı kullanın ve ana UI iş parçacığını ana işini yapmak için serbest bırakın:
// StackSnippet can't fetch external files, so here we have to build it
// in your case you'd just have it in an external file
const worker_script = `
function FOR(rounds) {
var x = 0;
for (var i = 0; i < rounds; i++) {
x += Math.sqrt(i);
}
return (x);
}
self.onmessage = (evt) => {
// post back the result
self.postMessage( FOR( evt.data ) );
}`;
const worker_blob = new Blob([worker_script],{type:"text/javascript"});
const worker_url = URL.createObjectURL(worker_blob);
// initialise the worker
const worker = new Worker(worker_url);
const button = document.querySelector("button");
function pressed() {
var text = document.getElementById("text");
text.innerHTML = "i have changed " + new Date().getTime();
// handle the results from the worker
worker.addEventListener("message", (evt) => {
console.log(evt.data);
console.log("processing done");
button.disabled = false;
}, {once: true} );
// start the computation
worker.postMessage(2000000000);
button.disabled = true;
console.log("new computation started");
}
<button onclick="pressed()">press here</button>
<span id="text">still the same text</span>
Ancak tek bir zaman aşımını beklemenin veya mikro görev için daha da kötüsü beklemenin işe yaramayacağına dikkat edin. SetTimeout'u kullanarak paiting çerçevesinin (normalde monitörün yenileme hızıyla aynı frekansta, yani çoğu genel durumda her 16,6 ms'de bir) oluşacağından emin olamazsınız. Mikro görevler kullanarak bunun gerçekleşmeyeceğinden emin olabilirsiniz. Mikro görevin işleme öncesinde yürütüldüğünü kanıtlayan herhangi bir örnek var mı? .
Bunun nedeni, tarayıcının işlenen sayfaya herhangi bir değişiklik uygulamadan önce mevcut çağrı yığınının (birbirini çağıran işlevler) tamamen çözülmesini beklemesidir. Bu sorunu çözmek için, geri arama kuyruğundaki ikinci işlevi bir setTimeoutveya ile ertelemelisiniz queueMicrotask. Bunu dev araçları açıkken çalıştırmayı deneyebilirsiniz ve kesme noktasına ulaşıldığında aralığın değiştiğini fark edeceksiniz:
<html>
<body>
<button onclick="pressed()">press here</button>
<span id="text">still the same text</span>
<script>
function FOR(rounds) {
var x = 0;
for (var i = 0; i < rounds; i++) {
x += Math.sqrt(i);
}
return (x);
}
function pressed() {
var text = document.getElementById("text");
text.innerHTML = "i have changed";
queueMicrotask(function () {
debugger;
console.log(FOR(2000000000));
console.log("processing done");
});
}
</script>
</body>
</html>