Maison  >  Article  >  interface Web  >  jssetTimeOut()application

jssetTimeOut()application

hzc
hzcavant
2020-07-03 10:51:572374parcourir

setTimeout, une fonction avec laquelle les ingénieurs front-end doivent gérer. Elle a l'air très simple et simple. Elle a un nom très extraordinaire - timer Quand j'étais jeune, je pensais naïvement que je pouvais contrôler l'avenir. Je ne sais pas à quel point c'est simple. Il y a un secret choquant caché dedans. Je me souviens encore de la première fois que j'ai utilisé cette fonction, je pensais naïvement que c'était un outil pour réaliser du multi-threading en js. pour mettre en œuvre un petit jeu sur les batailles de chars, et c'était difficile à jouer. Mais à mesure que j'avance sur la route du front-end, ma compréhension commence à changer. avec un voile, et il y a souvent des comportements étranges que je n'arrive pas à comprendre. Finalement, ma patience épuisée, je me suis décidée à arracher son masque et à découvrir.

Pour parler. concernant l'origine de setTimeout, nous devons commencer par sa définition officielle. C'est ainsi que le w3c le définit

La méthode setTimeout() est utilisée pour appeler une fonction ou une expression calculée après un nombre spécifié de millisecondes. .

En voyant une telle description, nous comprenons qu'il s'agit d'une minuterie, et la fonction que nous définissons est un "réveil", qui sera exécuté lorsque le temps sera écoulé. Cependant, si vous êtes intelligent. , vous ne pouvez pas vous empêcher de penser comme ça. Une question, et si c'était settimeout(fn,0) ? D'après la définition, la pratique est-elle le seul critère pour tester la vérité ? l'expérience suivante

<!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>

C'est une expérience très simple. Si settimeout(0) est exécuté immédiatement, alors le résultat de l'exécution ici devrait être 1->2>3. Cependant, le résultat réel est 1. ->3->2. Cela montre que settimeout (0) n'est pas exécuté immédiatement. En même temps, cela nous fait bizarre à propos du comportement de settimeout.

Le moteur js est. exécuté dans un seul thread

Abordons d'abord les problèmes ci-dessus. Laissons cela de côté et voyons si nous pouvons trouver des indices sur la conception du langage js

Nous avons trouvé qu'un. Un point très important dans la conception du langage js est que js n'a pas de multi-thread. L'exécution du moteur js est monothread. Cette fonctionnalité me dérange depuis longtemps. js est monothread, qui chronométrera le timer ? Qui enverra la requête ajax ? Je suis tombé dans un angle mort, c'est-à-dire que js est équivalent au navigateur. le navigateur lui-même. Le moteur js est monothread, mais le navigateur peut être multithread. Le moteur js n'est qu'un thread du navigateur, les requêtes réseau, le rendu du navigateur, etc. Sans aucune preuve, regardons quand même un exemple

<!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 est vrai par défaut, et c'est une boucle infinie en while Enfin L'alerte ne sera pas exécutée et j'ai changé isEnd en false après. 1 seconde. Si le moteur js est multithread, l'alerte sera exécutée après 1 seconde. Cependant, la situation réelle est que la page bouclera pour toujours. L'alerte n'est pas exécutée. C'est une bonne preuve que settimeout ne peut pas être exécuté. utilisé comme multi-thread. L'exécution du moteur js est monothread

boucle d'événement

D'après l'expérience ci-dessus, nous sommes encore plus confus, que fait exactement. settimeout faire ?

Il s'avère que nous devons encore trouver la réponse à partir de la conception du langage js.

Le moteur js est exécuté dans un Il est basé sur un langage événementiel et sa séquence d'exécution suit un mécanisme appelé file d'attente d'événements. Sur l'image, nous pouvons voir que les navigateurs ont différents threads, tels que des déclencheurs d'événements, des requêtes réseau, des minuteries, etc. les threads sont tous basés sur des événements. Lorsque le moteur js traite le code lié à d'autres threads, il sera distribué aux autres threads une fois traités, js est nécessaire. Lorsque le moteur calcule, il ajoute une tâche à la file d'attente des événements. ce processus, js ne bloque pas le code et attend que les autres threads terminent l'exécution, et une fois l'exécution des autres threads terminée, la tâche événementielle est ajoutée pour indiquer au moteur js d'effectuer les opérations pertinentes. Il s'agit de la programmation asynchrone du modèle js.

Ainsi, lorsque nous reviendrons sur settimeout(0), nous réaliserons soudainement que lorsque le code js est exécuté ici, un thread de minuterie sera démarré, puis le code suivant continuera à être exécuté. être exécuté à l'heure spécifiée Ensuite, insérez une tâche dans la file d'attente des événements. On peut voir que les opérations dans settimeout(0) seront placées après toutes les tâches du thread principal. Cela explique également pourquoi le premier résultat expérimental est 1->3-. 2.

On voit que la définition officielle de settimeout prête à confusion. Une nouvelle définition devrait être donnée :

Dans le temps spécifié, mettre le . tâche dans la file d'attente des événements, en attendant que le moteur js soit exécuté après son inactivité.

Le moteur js et le moteur GUI s'excluent mutuellement

À ce sujet, nous devons parler d'un autre moteur du navigateur --- Moteur de rendu GUI. L'opération de rendu dans js est également asynchrone, par exemple, le code d'opération DOM générera une tâche dans l'événement. file d'attente, et lorsque js exécute cette tâche, il appellera le rendu du moteur GUI .

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教程

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer