Heim  >  Artikel  >  Web-Frontend  >  js setTimeOut()-Anwendung

js setTimeOut()-Anwendung

hzc
hzcnach vorne
2020-07-03 10:51:572329Durchsuche

setTimeout, eine Funktion, mit der sich Front-End-Ingenieure befassen müssen. Sie hat einen sehr außergewöhnlichen Namen: Als ich jung war, dachte ich, ich könnte die Zukunft kontrollieren Ich weiß nicht, wie einfach es ist. Ich erinnere mich noch an das erste Mal, als ich diese Funktion benutzte. Ich dachte naiv, es sei ein Werkzeug zur Realisierung von Multithreading in js Ein kleines Spiel über Panzerschlachten zu implementieren, und es war schwierig, es zu spielen. Aber je weiter ich auf dem Weg dorthin voranschreite, desto mehr scheint sich mein Verständnis davon zu ändern mit einem Schleier, und es gibt oft einige seltsame Verhaltensweisen, die ich nicht verstehen kann. Schließlich war meine Geduld erschöpft, ich beschloss, seine Maske abzureißen und es herauszufinden.

Um zu reden Was den Ursprung von setTimeout betrifft, müssen wir mit seiner offiziellen Definition beginnen. So definiert w3c es.

Die Methode setTimeout() wird verwendet, um eine Funktion oder einen berechneten Ausdruck nach einer bestimmten Anzahl von Millisekunden aufzurufen .

Wenn wir eine solche Beschreibung sehen, verstehen wir, dass es sich um einen Timer handelt und dass die von uns eingestellte Funktion ein „Wecker“ ist, der jedoch ausgeführt wird, wenn die Zeit abgelaufen ist , Sie können nicht anders, als so zu denken. Was ist, wenn es settimeout(fn,0) ist? Wird es gemäß der Definition sofort ausgeführt? Das folgende Experiment

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <script>
        alert(1); 
        setTimeout("alert(2)", 0); 
        alert(3); 
    </script>
</body>
</html>

Dies ist ein sehr einfaches Experiment. Wenn settimeout(0) sofort ausgeführt wird, sollte das Ausführungsergebnis hier 1->2>3 sein. Das tatsächliche Ergebnis ist jedoch 1 ->3->2. Dies zeigt, dass settimeout (0) nicht sofort ausgeführt wird. Gleichzeitig fühlen wir uns seltsam über das Verhalten von settimeout in einem einzigen Thread ausgeführt

Lassen Sie uns zunächst die oben genannten Probleme angehen. Lassen Sie uns es beiseite legen und sehen, ob wir Hinweise aus dem Design der js-Sprache finden können

Wir haben festgestellt, dass a Ein sehr wichtiger Punkt beim Design der js-Sprache ist, dass js kein Multithreading hat. Diese Funktion hat mich seit langem nicht beunruhigt js ist Single-Threaded. Wer wird die Ajax-Anfrage senden? Das heißt, js ist äquivalent zum Browser. Wir sind es gewohnt, Code im Browser auszuführen, aber ignorieren Der Browser selbst ist ein Single-Thread, der Browser kann jedoch nur ein Thread des Browsers sein. Timer-Timing, Netzwerkanforderungen, Browser-Rendering usw. werden von verschiedenen Threads ausgeführt. Schauen wir uns ohne Beweise noch ein Beispiel an.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
</body>
<script>
    var isEnd = true;
    window.setTimeout(function () {
        isEnd = false;//1s后,改变isEnd的值
    }, 1000);
    while (isEnd);
    alert('end');
</script>
</html>
isEnd ist standardmäßig wahr und es handelt sich um eine Endlosschleife. Schließlich wird der Alarm nicht ausgeführt und danach isEnd in false geändert 1 Sekunde. Wenn die js-Engine mehrere Threads verwendet, wird die Warnung tatsächlich nach 1 Sekunde ausgeführt. Dies ist ein guter Beweis dafür, dass settimeout nicht verwendet werden kann als Multi-Thread. Die Ausführung der js-Engine erfolgt als Single-Thread tun?

Es stellt sich heraus, dass wir noch die Antwort aus dem Design der js-Sprache finden müssen.

Die js-Engine wird in einem einzigen ausgeführt Thread. Seine Ausführungssequenz folgt einem Mechanismus namens Ereigniswarteschlange. Auf dem Bild können wir sehen, dass Browser über verschiedene Threads verfügen, z. B. über Ereignisauslöser, Netzwerkanforderungen usw. Die Verbindungen zwischen Threads basieren alle auf Ereignissen. Wenn die js-Engine den mit anderen Threads verbundenen Code verarbeitet, wird sie nach der Verarbeitung von js zur Ereigniswarteschlange hinzugefügt Prozess, js blockiert den Code nicht und wartet darauf, dass andere Threads die Ausführung abschließen. Nachdem andere Threads die Ausführung abgeschlossen haben, wird eine Ereignisaufgabe hinzugefügt, um die js-Engine anzuweisen, relevante Vorgänge auszuführen. Dies ist die asynchrone Programmierung des js-Modells.

Wenn wir also auf settimeout(0) zurückblicken, werden wir plötzlich feststellen, dass ein Timer-Thread gestartet wird und der folgende Code weiterhin ausgeführt wird Zur angegebenen Zeit ausgeführt Fügen Sie dann eine Aufgabe in die Ereigniswarteschlange ein. Es ist ersichtlich, dass die Operationen in settimeout(0) nach allen Haupt-Thread-Aufgaben platziert werden. Dies erklärt auch, warum das erste experimentelle Ergebnis 1->3-2 ist .

Es ist ersichtlich, dass die offizielle Definition von settimeout verwirrend ist. Es sollte eine neue Definition gegeben werden:

Stellen Sie die Aufgabe

in der angegebenen Zeit ein in die Ereigniswarteschlange und wartet darauf, dass die js-Engine ausgeführt wird, nachdem sie inaktiv ist.

Die js-Engine und die GUI-Engine schließen sich gegenseitig aus

Wenn es darum geht, müssen wir über eine andere Engine des Browsers sprechen: Der Rendering-Vorgang in js ist ebenfalls asynchron. Beispielsweise generiert der DOM-Operationscode eine Aufgabe in der Ereigniswarteschlange , und wenn js diese Aufgabe ausführt, ruft es das GUI-Engine-Rendering auf.

js语言设定js引擎与GUI引擎是互斥的,也就是说GUI引擎在渲染时会阻塞js引擎计算.原因很简单,如果在GUI渲染的时候,js改变了dom,那么就会造成渲染不同步. 我们需要深刻理解js引擎与GUI引擎的关系,因为这与我们平时开发息息相关,我们时长会遇到一些很奇葩的渲染问题.看这个例子

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <table border=1>
        <tr><td><button id=&#39;do&#39;>Do long calc - bad status!</button></td>
            <td><p id=&#39;status&#39;>Not Calculating yet.</p></td>
        </tr>
        <tr><td><button id=&#39;do_ok&#39;>Do long calc - good status!</button></td>
            <td><p id=&#39;status_ok&#39;>Not Calculating yet.</p></td>
        </tr>
    </table>    
<script>
function long_running(status_p) {
    var result = 0;
    for (var i = 0; i < 1000; i++) {
        for (var j = 0; j < 700; j++) {
            for (var k = 0; k < 300; k++) {
                result = result + i + j + k;
            }
        }
    }
    document.querySelector(status_p).innerHTML = 'calclation done' ;
}
document.querySelector('#do').onclick = function () {
    document.querySelector('#status').innerHTML = 'calculating....';
    long_running('#status');
};
document.querySelector('#do_ok').onclick = function () {
    document.querySelector('#status_ok').innerHTML = 'calculating....';
    window.setTimeout(function (){ long_running('#status_ok') }, 0);
};
</script>
</body>
</html>

我们希望能看到计算的每一个过程,我们在程序开始,计算,结束时,都执行了一个dom操作,插入了代表当前状态的字符串,Not Calculating yet.和calculating....和calclation done.计算中是一个耗时的3重for循环. 在没有使用settimeout的时候,执行结果是由Not Calculating yet 直接跳到了calclation done.这显然不是我们希望的.而造成这样结果的原因正是js的事件循环单线程机制.dom操作是异步的,for循环计算是同步的.异步操作都会被延迟到同步计算之后执行.也就是代码的执行顺序变了.calculating....和calclation done的dom操作都被放到事件队列后面而且紧跟在一起,造成了丢帧.无法实时的反应.这个例子也告诉了我们,在需要实时反馈的操作,如渲染等,和其他相关同步的代码,要么一起同步,要么一起异步才能保证代码的执行顺序.在js中,就只能让同步代码也异步.即给for计算加上settimeo0t.

settimeout(0)的作用

不同浏览器的实现情况不同,HTML5定义的最小时间间隔是4毫秒. 使用settimeout(0)会使用浏览器支持的最小时间间隔.所以当我们需要把一些操作放到下一帧处理的时候,我们通常使用settimeout(0)来hack.

requestAnimationFrame

这个函数与settimeout很相似,但它是专门为动画而生的.settimeout经常被用来做动画.我们知道动画达到60帧,用户就无法感知画面间隔.每一帧大约16毫秒.而requestAnimationFrame的帧率刚好是这个频率.除此之外相比于settimeout,还有以下的一些优点:

  • requestAnimationFrame 会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率,一般来说,这个频率为每秒60帧,每帧大约16毫秒.

  • 在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流,这当然就意味着更少的的cpu,gpu和内存使用量。

  • 但它优于setTimeout/setInterval的地方在于它是由浏览器专门为动画提供的API,在运行时浏览器会自动优化方法的调用,并且如果页面不是激活状态下的话,动画会自动暂停,有效节省了CPU开销。

总结:

  1. 浏览器的内核是多线程的,它们在内核制控下相互配合以保持同步,一个浏览器至少实现三个常驻线程:javascript引擎线程,GUI渲染线程,浏览器事件触发线程。

  2. javascript引擎是基于事件驱动单线程执行的.JS引擎一直等待着任务队列中任务的到来,然后加以处理,浏览器无论什么时候都只有一个JS线程在运行JS程序。

  3. 当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行。但需要注意 GUI渲染线程与JS引擎是互斥的,当JS引擎执行时GUI线程会被挂起,GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。

  4. 当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理。这些事件可来自JavaScript引擎当前执行的代码块如setTimeOut、也可来自浏览器内核的其他线程如鼠标点击、AJAX异步请求等,但由于JS的单线程关系所有这些事件都得排队等待JS引擎处理。

推荐教程:《JS教程

Das obige ist der detaillierte Inhalt vonjs setTimeOut()-Anwendung. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:cnblogs.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen