Heim  >  Artikel  >  Web-Frontend  >  Einführung in den JavaScript-Single-Thread-Mechanismus und das setTimeout-Ausführungsprinzip (mit Code)

Einführung in den JavaScript-Single-Thread-Mechanismus und das setTimeout-Ausführungsprinzip (mit Code)

不言
不言nach vorne
2019-03-27 09:09:013369Durchsuche

Der Inhalt dieses Artikels ist eine Einführung in den JavaScript-Single-Thread-Mechanismus und das setTimeout-Ausführungsprinzip (mit Code). Ich hoffe, dass er für Sie hilfreich ist.

Einzelthread-Mechanismus der Javascript-Engine

Zunächst ist klar, dass es sich bei der JavaScript-Engine um einen Single-Thread-Mechanismus handelt.

JavaScript wird in einem einzelnen Thread ausgeführt und kann nicht mehrere Codeteile gleichzeitig ausführen. Wenn ein bestimmter Code ausgeführt wird, müssen alle nachfolgenden Aufgaben warten und eine Aufgabenwarteschlange bilden. Sobald die aktuelle Aufgabe abgeschlossen ist, wird die nächste Aufgabe aus der Warteschlange genommen, was oft als „Blockierung der Ausführung“ bezeichnet wird.

kann so verstanden werden: Asynchroner Code wird nur ausgeführt, wenn im JS-Thread kein synchroner Code ausgeführt werden kann.

Also ein Mausklick oder der Timer erreicht den Zeitpunkt oder die Ajax-Anfrage ist abgeschlossen. Wenn eine Rückruffunktion ausgelöst wird, werden diese Ereignishandler oder Rückruffunktionen nicht sofort ausgeführt, sondern sofort in die Warteschlange gestellt und ausgeführt, sobald der Thread frei ist. Wenn der aktuelle JavaScript-Thread einen zeitaufwändigen Code ausführt und ein Mausklick erfolgt, wird der Event-Handler blockiert und der Benutzer kann das Feedback nicht sofort sehen. Der Event-Handler wird bis zum vorherigen in die Task-Warteschlange gestellt Die Ausführung beginnt erst, wenn der Code endet. Wenn im Code ein setTimeout festgelegt ist, fügt der Browser den Code zum entsprechenden Zeitpunkt in die Aufgabenwarteschlange ein. Wenn diese Zeit auf 0 gesetzt ist, bedeutet dies, dass er sofort in die Warteschlange eingefügt, aber nicht ausgeführt wird Sie müssen noch auf die Ausführung des vorherigen Codes warten. Daher kann setTimeout die Ausführungszeit nicht garantieren. Ob die Ausführung rechtzeitig erfolgt, hängt davon ab, ob der JavaScript-Thread überfüllt oder inaktiv ist.

Der Multithreading-Mechanismus und die Ereignisschleife des Browsers

Zunächst ist klar, dass der Kernel des Browsers unter der Kontrolle des Kernels miteinander kooperiert, um die Synchronisation aufrechtzuerhalten Browser mindestens drei residente Threads implementieren:

Javascript-Engine-Thread

GUI-Rendering-Thread

Browser-Ereignis-Trigger-Thread

JavaScript-Engine ist Single- Threaded Beim Ausführen verfügt der Browser zu jedem Zeitpunkt nur über einen einzigen Thread, der das JavaScript-Programm ausführt.

Die JavaScript-Engine basiert auf der ereignisgesteuerten Single-Thread-Ausführung. Die JS-Engine hat auf das Eintreffen von Aufgaben gewartet in der Aufgabenwarteschlange und fügt dann die Verarbeitung hinzu, sodass der Browser zu jedem Zeitpunkt nur einen JS-Thread hat, der das JS-Programm ausführt.

Der GUI-Rendering-Thread ist für das Rendern der Browseroberfläche verantwortlich. Wenn die Schnittstelle neu gezeichnet werden muss (Repaint) oder ein Reflow (Reflow) durch einen Vorgang verursacht wird, wird dieser Thread ausgeführt. Es ist jedoch zu beachten, dass sich der GUI-Rendering-Thread und die JS-Engine gegenseitig ausschließen. Wenn die JS-Engine ausgeführt wird, wird der GUI-Thread angehalten und GUI-Updates werden in einer Warteschlange gespeichert und sofort ausgeführt, wenn die JS-Engine ausgeführt wird Leerlauf.

Ereignisauslösender Thread Wenn ein Ereignis ausgelöst wird, fügt der Thread das Ereignis am Ende der ausstehenden Warteschlange hinzu und wartet auf die Verarbeitung durch die JS-Engine. Diese Ereignisse können aus dem Codeblock stammen, der derzeit von der JavaScript-Engine ausgeführt wird, z. B. setTimeOut, oder aus anderen Threads im Browserkernel, z. B. Mausklicks, asynchronen AJAX-Anforderungen usw. Aufgrund der Single-Thread-Beziehung von JS jedoch alle Diese Ereignisse müssen zur Verarbeitung durch die JS-Engine in die Warteschlange gestellt werden. (Asynchroner Code wird ausgeführt, wenn im Thread kein synchroner Code ausgeführt wird)

Ereignisschleife (Ereignisschleife): Wird zum Verwalten unseres asynchronen Codes verwendet und legt ihn in einen Thread-Pool. Unter ihnen

Das Implementierungsprinzip von setTimeout in JavaScript

Zunächst ist klar, dass die setTimeout-Funktion ein asynchroner Code ist, aber tatsächlich ist setTimeout keine echte asynchrone Operation

Aus diesem Grund Der Arbeitsmechanismus von JS-Threads: Asynchroner Code wird ausgeführt, wenn im Thread kein synchroner Code ausgeführt wird. setTimeout ist ein asynchroner Code, daher kann setTimeout nur ausgeführt werden, wenn js inaktiv ist

Wie bereits erwähnt, wenn a Wenn im Code setTimeout festgelegt ist, fügt der Browser den Code zum entsprechenden Zeitpunkt in die Aufgabenwarteschlange ein. Wenn diese Zeit auf 0 gesetzt ist, bedeutet dies, dass er sofort in die Warteschlange eingefügt, aber nicht sofort ausgeführt wird . Es muss noch auf die Ausführung des vorherigen Codes gewartet werden. Daher kann setTimeout die Ausführungszeit nicht garantieren. Ob die Ausführung rechtzeitig erfolgt, hängt davon ab, ob der JavaScript-Thread überfüllt oder inaktiv ist.

Das heißt, setTimeout kann nur garantieren, dass die Aufgabe (Funktion, die ausgeführt werden muss) in die Warteschlange eingefügt wird, um nach der angegebenen Zeit zu warten. Es kann nicht garantiert werden, wann die Aufgabe ausgeführt wird. Der Thread, der JavaScript ausführt, nimmt die Aufgabe aus der Warteschlange und führt sie aus, wenn sie inaktiv ist. JavaScript nutzt diesen Warteschlangenmechanismus, um uns die Illusion einer asynchronen Ausführung zu vermitteln.

Manchmal wird der Code in setTimeout schnell ausgeführt, und wir haben das Gefühl, dass dieser Code asynchron ausgeführt wird. Dies liegt daran, dass der JavaScript-Thread nicht aufgrund zeitaufwändiger Vorgänge blockiert ist und daher schnell entfernt werden kann aus der Warteschlange wird dann die Aufgabe in der Warteschlange ausgeführt.

Beispielanalyse

Nachdem wir die obige theoretische Grundlage haben, analysieren wir die folgenden Beispiele:

======= ==== ==============================

var t = true;
    
window.setTimeout(function (){
    t = false;
},1000);
    
while (t){}
    
alert('end');

Laufergebnis: Das Programm fällt in eine Endlosschleife. t = false kann nicht ausgeführt werden, daher wird alert('end') nicht ausgeführt.
Analyse:

JS是单线程的,所以会先执行 while(t){} 再 alert,但这个循环体是死循环,所以永远不会执行alert。

为什么不执行 setTimeout?是因为JS的工作机制是:当线程中没有执行任何同步代码的前提下才会执行异步代码,setTimeout是异步代码,所以 setTimeout 只能等JS空闲才会执行,但死循环是永远不会空闲的,所以 setTimeout 也永远得不到执行。

===========================================

var start = new Date();
    
setTimeout(function(){  
    var end = new Date();  
    console.log("Time elapsed: ", end - start, "ms");  
}, 500);  
      
while (new Date - start <= 1000){}

运行结果:"Time elapsed: 1035 ms" (这里的1035不准确 但是一定是大于1000的)
解析:

JS是单线程 setTimeout 异步代码 其回调函数执行必须需等待主线程运行完毕

当while循环因为时间差超过 1000ms 跳出循环后,setTimeout 函数中的回调才得以执行

===========================================

for(var i=0;i<10;i++){
    setTimeout(function() {
        console.log(i);
    }, 0);
}

运行结果:输出10个10
解析:JS单线程 setTimeout 异步代码 任务队列
问:如何修改可以使上述代码输出 0123456789
自执行函数 或 使用ES6中的let关键字

// 自执行函数 形成闭包 记忆其被创建时的环境
for(var i=0;i<10;i++){
    setTimeout((function() {
         console.log(i);
    })(), 0);
}

setTimeout(0)函数的作用

现在我们了解了setTimeout函数执行的原理,那么它有什么作用呢?
setTimeout函数增加了Javascript函数调用的灵活性,为函数执行顺序的调度提供极大便利。
简言之,改变顺序,这正是setTimeout(0)的作用。

使用场景示例:

<input type="text" onkeydown="show(this.value)">  
<p></p>  
<script type="text/javascript">  
  function show(val) {  
    document.getElementsByTagName('p')[0].innerHTML = val;  
  }  
</script>

这里绑定了 keydown 事件,意图是当用户在文本框里输入字符时,将输入的内容实时地在

中显示出来。但是实际效果并非如此,可以发现,每按下一个字符时,

中只能显示出之前的内容,无法得到当前的字符。

修改代码:

  <input type="text" onkeydown="var self=this; setTimeout(function(){show(self.value)}, 0)">  
  <p></p>  
  <script type="text/javascript">  
    function show(val) {  
      document.getElementsByTagName('p')[0].innerHTML = val;  
    }  
  </script>

这段代码使用setTimeout(0)就可以实现需要的效果了。

这里其实涉及2个任务,1个是将键盘输入的字符回写到输入框中,一个是获取文本框的值将其写入p中。第一个是浏览器自身的默认行为,一个是我们自己编写的代码。很显然,必须要先让浏览器将字符回写到文本框,然后我们才能获取其内容写到p中。改变顺序,这正是setTimeout(0)的作用。

其他应用场景:有时候,加载一些广告的时候,我们用setTimeout实现异步,好让广告不会阻塞我们页面的渲染。

setTimeout 和 setInterval 在执行异步代码的时候有着根本的不同

如果一个计时器被阻塞而不能立即执行,它将延迟执行直到下一次可能执行的时间点才被执行(比期望的时间间隔要长些)

如果setInterval回调函数的执行时间将足够长(比指定的时间间隔长),它们将连续执行并且彼此之间没有时间间隔。

本篇文章到这里就已经全部结束了,更多其他精彩内容可以关注PHP中文网的JavaScript视频教程栏目!

Das obige ist der detaillierte Inhalt vonEinführung in den JavaScript-Single-Thread-Mechanismus und das setTimeout-Ausführungsprinzip (mit Code). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

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