English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Approfondimento del meccanismo di temporizzazione in JavaScript

Questo articolo introduce il meccanismo di temporizzazione di JavaScript, per comprendere il meccanismo di temporizzazione di JavaScript, è necessario conoscere il meccanismo di esecuzione di JavaScript.

Prima di tutto, devo dichiarare che JavaScript esegue in modo singolo (thread del motore JavaScript) e驱动的 da eventi.

Primo, ci sono molti thread nel browser

I thread più basilari contenuti in un browser:

1, thread del motore JavaScript.

2, thread del timer, setInterval e setTimeout scatenano questo thread.

3, thread di scatenamento degli eventi del browser, questo thread scatena eventi come onclick, onmousemove e altri eventi del browser.

4, thread di rendering dell'interfaccia utente, responsabile del rendering degli elementi HTML del browser. Attenzione: durante l'esecuzione dello script dal motore JavaScript, il thread di rendering dell'interfaccia utente è in stato di sospensione. Questo significa che quando si utilizza JavaScript per operare sui nodi dell'interfaccia, non si vedono immediatamente i risultati, ma solo quando il thread del motore JavaScript è libero (questo viene detto alla fine).

5, thread di richiesta HTTP (le richieste Ajax sono incluse).

Questi thread, sotto il controllo del nucleo del browser, si coordinano tra loro per completare il lavoro (non so esattamente cosa).

Secondo, coda dei compiti

Sappiamo che JavaScript è un thread singolo, tutti i codici JavaScript vengono eseguiti nel thread del motore JavaScript. Nell'articolo di Mr. Ruan Yifeng, questo thread viene chiamato thread principale, che è uno stack di esecuzione. (Il contenuto seguente è principalmente basato sull'articolo di Mr. Ruan Yifeng e riassunto.)

Questi codici JavaScript possiamo considerarli come compiti singoli, che si distinguono in compiti同步i e asincroni. I compiti同步i (ad esempio, istruzioni di assegnazione di variabili, istruzioni alert, istruzioni di dichiarazione di funzione, ecc.) vengono eseguiti direttamente in modo sequenziale sulla linea principale. I compiti asincroni (ad esempio, vari eventi scatenati dalla linea di eventi del browser, risposte del server tramite Ajax, ecc.) si mettono in coda nell'ordine temporale (che può anche essere chiamata coda di eventi, coda di messaggi) e aspettano di essere eseguiti. Appena vengono completati i compiti sulla linea principale, viene controllata la coda di compiti per vedere se ci sono compiti in coda in attesa, e se ci sono, questi entrano nella linea principale per essere eseguiti.

Ad esempio, ecco un esempio di seguito:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="larghezza=device-width, scala_iniziale=1">
<title>Meccanismo di temporizzazione</title>
<style type="text/css">
body{
  margine: 0;
  spaziatura: 0;
  posizione: relativa;
  altezza: 600px;
}
#test{
  height: 30px;
  width: 30px;
  position: absolute;
  left: 0;
  top: 100px;
  background-color: pink;
}
</style>
</head>
<body>
  <div id="test">
  </div>
<script>
  var pro = document.getElementById('test');
  pro.onclick = function() {}}
    alert('Non sono stato eseguito immediatamente.');
  };
  function test() {
    a = new Date();
    var b=0;
   for(var i=0;i<3000000000;i++) {
     b++;
   }
   c = new Date();
   console.log(c-a);
  }
 test();
</script>
</body>
</html>

In questo esempio, l'esecuzione della funzione test() richiede circa 8-9 secondi, quindi quando apriamo questa pagina, se clicchiamo il quadrato rosa prima di 8 secondi, non verrà visualizzato immediatamente il messaggio di avviso, ma dopo 8 secondi. E il numero di clic prima di 8 secondi corrisponde al numero di messaggi di avviso visualizzati dopo 8 secondi.

Quando apriamo questa pagina, la thread principale dichiara prima la funzione test, poi la variabile pro, assegna il nodo p al pro, aggiunge l'evento click al nodo p e specifica la funzione di callback (sospesa), poi chiama la funzione test e esegue il codice al suo interno. Durante l'esecuzione del codice nella funzione test, clicchiamo il nodo p, la thread degli eventi del browser rileva questo evento e lo mette nella coda dei task, in modo che la thread principale, una volta completati i task (qui la funzione test), controlli la coda dei task e trovi questo evento per eseguire la funzione di callback corrispondente. Se clicchiamo più volte, questi eventi multiple vengono messi in coda secondo l'ordine di attivazione (puoi aggiungere un altro elemento cliccabile e alternare tra diversi elementi per verificare).

Di seguito è riassunto il meccanismo di esecuzione dei task:

Il meccanismo di esecuzione asincrona è il seguente. (L'esecuzione sincrona è la stessa, poiché può essere considerata un'esecuzione asincrona senza task asincroni.)

1, tutti i task sincroni vengono eseguiti sulla thread principale, formando uno stack di esecuzione (execution context stack).

2, oltre alla thread principale, esiste anche una coda dei task (task queue). Appena un task asincrono ha un risultato di esecuzione, mette un evento nella coda dei task.

3, una volta completati tutti i task sincroni nello stack di esecuzione, il sistema legge il coda dei task per vedere quali eventi ci sono. Quelli che corrispondono ai task asincroni, quindi finisce lo stato di attesa e entra nello stack di esecuzione per iniziare l'esecuzione.

4, la thread principale ripete continuamente il passo terzo sopra.

Tre, eventi e funzioni di callback

Quando assegniamo un evento a un elemento DOM, assegniamo sempre una funzione di callback per eseguire il codice corrispondente quando l'evento si verifica veramente.

Le funzioni di callback degli eventi nella thread principale vengono sospese, se ci sono eventi corrispondenti in coda nei task, quando la thread principale li rileva, esegue le funzioni di callback corrispondenti. Possiamo anche dire che la thread principale esegue task asincroni, che è eseguire le funzioni di callback corrispondenti.

Quattro, ciclo degli eventi

Il processo di verifica della thread principale sugli eventi nel coda dei task è un ciclo continuo, quindi possiamo disegnare una figura del ciclo degli eventi:

Nella figura sopra, la thread principale genera il heap e l'esecuzione dello stack, una volta completato il task nello stack, la thread principale verifica gli eventi nel coda dei task inseriti da altre thread, rileva l'evento in testa e trova la funzione di callback corrispondente tra le funzioni di callback sospese, quindi l'esegue nello stack di esecuzione, e questo processo si ripete continuamente.

V. Timer

结合以上知识,下面探讨JavaScript中的定时器:setTimeout()和setInterval()。

setTimeout(func, t)是超时调用,间隔一段时间后调用函数。这个过程在事件循环中的过程如下(我的理解):

主线程执行完setTimeout(func, t);语句后,把回调函数func挂起,同时定时器线程开始计时,当计时等于t时,相当于发生了一个事件,这个事件传入任务队列(结束计时,只计时一次),当主线程中的任务执行完后,主线程检查任务队列发现了这个事件,就执行挂起的回调函数func。我们指定的时间间隔t只是参考值,真正的时间间隔取决于执行完setTimeout(func, t);语句后的代码所花费的时间,而且是只大不小。(即使我们把t设为0,也要经历这个过程)。

setInterval(func, t)是间歇调用,每隔一段时间后调用函数。这个过程在事件循环中的过程与上面的类似,但又有所不同。

setTimeout()是在时间t后定时器线程在任务队列中添加一个事件(注意是一个),而setInterval()是每经过时间t(一直在计时,除非清除间歇调用)后定时器线程在任务队列中添加一个事件,而不管之前添加的事件有没有被主线程检测到并执行。(实际上浏览器是比较智能的,浏览器在处理setInterval的时候,如果发现已任务队列中已经有排队的同一ID的setInterval的间歇调用事件,就直接把新来的事件 Kill 掉。也就是说任务队列中一次只能存在一个来自同一ID的间歇调用的事件。)

Per esempio, se il codice dopo setInterval(func, t); richiede 2t di tempo, dopo 2t di tempo, la主线程从任务队列中检测到定时器线程传入的第一个间歇调用事件,func开始执行。当第一次的func执行完毕后,第二次的间歇调用事件早已传入任务队列,主线程马上检测到第二次的间歇调用事件,func函数又立即执行。在这种情况下,func函数的两次执行是连续发生的,中间没有时间间隔。

Ecco un esempio:

function test() {
    a = new Date();
    var b=0;
   for(var i=0;i<3000000000;i++) {
     b++;
   }
   c = new Date();
   console.log(c-a);
 }
  function test2() {
   var d = new Date().valueOf();
   //var e = d-a;
   console.log('Il momento in cui sono stato chiamato è: '+d+'ms');
   //alert(1);
  }
  setInterval(test2,3000);
 test();

Risultato:

Perché dopo 8.6 secondi non è stato outputto due momenti identici, la risposta si può trovare nel contenuto menzionato sopra.

Il ciclo for dell'esempio ha richiesto 8601ms, durante l'esecuzione del ciclo for c'era solo un evento di chiamata intermittente in coda (per motivi come sopra menzionati). Dopo 8601ms, il primo evento di chiamata intermittente è entrato nella thread principale. Per questo esempio, in quel momento la coda dei task era vuota, quindi è stato possibile inserire un altro evento di chiamata intermittente. Quindi, al momento 1477462632228ms il secondo evento di chiamata intermittente (in realtà dovrebbe essere il terzo) è entrato nella coda dei task. Poiché lo stack di esecuzione della thread principale era vuoto, la thread principale ha eseguito immediatamente la funzione di callback corrispondente. La seconda chiamata è avvenuta con un intervallo di soli 320ms rispetto alla prima chiamata (in realtà 8601+320=8920, più o meno uguale a 9 secondi). Vediamo che la terza chiamata è tornata alla normalità perché nella thread principale non c'era altro codice, solo un task, ovvero eseguire la chiamata intermittente ogni tanto.

Esempio di chiamata intermittente implementata con setTimeout():

function test() {
    a = new Date();
    var b=0;
   for(var i=0;i<3000000000;i++) {
     b++;
   }
   c = new Date();
   console.log(c-a);
  }
  function test2(){
   var d = new Date().valueOf();
   console.log('Il momento in cui sono stato chiamato è: '+d+'ms');
   setTimeout(test2,3000);
  }
  setTimeout(test2,3000);
 test();

 Risultato:

L'intervallo di tempo tra due chiamate è基本上lo stesso. Pensa al motivo?

Vediamo un altro esempio:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="larghezza=device-width, scala_iniziale=1">
<title>Esercizio di Layout Flex</title>
<style type="text/css">
body{
  margine: 0;
  spaziatura: 0;
  posizione: relativa;
  altezza: 600px;
}
#test{
  height: 30px;
  width: 30px;
  position: absolute;
  left: 0;
  top: 100px;
  background-color: pink;
}
</style>
</head>
<body>
  <div id="test">
  </div>
<script>
 var p = document.createElement('p');
 p.style.width = '50px';
 p.style.height = '50px';
 p.style.border = '1px solid black';
 document.body.appendChild(p);
 alert('ok');
</script>
</body>
</html>

这个例子的结果是提示框先弹出,然后黑色边框的p元素才出现在页面中。原因很简单,就一句话:

在JavaScript引擎运行脚本期间,界面渲染线程都是处于挂起状态的。也就是说当使用JavaScript对界面中的节点进行操作时,并不会立即体现出来,要等到JavaScript引擎线程空闲时,才会体现出来。

以上就是我对JavaScript定时机制的理解及总结,如有错误,希望看到的大神指正。也希望大家多多支持呐喊教程。

声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:notice#oldtoolbag.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。

Ti potrebbe interessare