Table des matières
Scénario spécial 2 : Le délai minimum est de 4 ms
Scénario spécial 3 : Limitation du navigateur
async await机制
协程
单页面线程
Webworker线程
Maison interface Web js tutoriel Compréhension approfondie du modèle de concurrence et du mécanisme de boucle d'événements de JavaScript

Compréhension approfondie du modèle de concurrence et du mécanisme de boucle d'événements de JavaScript

Nov 27, 2019 pm 04:08 PM
javascript settimeout 协程 线程

Nous savons que le langage JS est une exécution en série, un blocage et une gestion des événements, alors comment prend-il en charge le traitement simultané des données ?

Compréhension approfondie du modèle de concurrence et du mécanisme de boucle d'événements de JavaScript

Langage "à thread unique"

Dans l'implémentation du navigateur, chaque page est indépendante processus, qui contient plusieurs threads tels que le moteur JS, le rendu de l'interface graphique, le déclenchement d'événements, les déclencheurs chronométrés, les requêtes HTTP asynchrones, etc.

Le processus est la plus petite unité d'allocation de ressources telle que le CPU dans le système d'exploitation. C'est l'entité d'exécution du programme et le conteneur de threads.
Le thread est la plus petite unité que le système d'exploitation peut effectuer la planification des opérations. Un thread fait référence à une seule séquence de flux de contrôle dans le processus.

Nous pouvons donc dire que JS est un langage « monothread ». Le code ne peut être exécuté qu'en série dans un seul ordre, et les autres codes seront bloqués avant la fin de l'exécution.

[Recommandations de cours associées : Tutoriel vidéo JavaScript]

Structure de données JS

Compréhension approfondie du modèle de concurrence et du mécanisme de boucle dévénements de JavaScript

Comme le montre la figure ci-dessus, il existe plusieurs structures de données importantes de JS :

● Pile : utilisée pour les appels de fonctions imbriqués de JS, dernier entré, premier sorti, jusqu'à ce que la pile soit effacé.

● Tas : zone de mémoire utilisée pour stocker de gros blocs de données, tels que des objets.

QuantityQueue : utilisé pour le mécanisme de boucle d'événements, premier entré, premier sorti, jusqu'à ce que la file d'attente soit vide.

Boucle d'événement

Notre expérience nous dit que JS peut être exécuté simultanément, comme les tâches planifiées et les requêtes AJAX simultanées. Alors, qu'est-ce que c'est. ? Qu'est-ce qui est fini ? En fait, tout cela est réalisé par JS en utilisant un seul thread pour simuler le multi-threading.

Compréhension approfondie du modèle de concurrence et du mécanisme de boucle dévénements de JavaScript

Comme le montre la figure ci-dessus, JS exécute la tâche du thread principal en série lorsqu'une tâche asynchrone telle qu'une minuterie est rencontrée, elle est placée dans la file d'attente des événements. la tâche du thread principal Une fois l'exécution terminée, accédez à la file d'attente des événements pour parcourir et retirer la première tâche à exécuter jusqu'à ce que la file d'attente soit vide.

Une fois toutes les exécutions terminées, il y aura un processus de surveillance principal qui détectera en permanence si la file d'attente est vide. Sinon, la boucle d'événements continue.

tâche planifiée setTimeout

La tâche planifiéesetTimeout(fn, timeout) sera d'abord transmise au module de minuterie du navigateur, et lorsque le délai est atteint up, Mettez l'événement dans la file d'attente des événements. Une fois l'exécution du thread principal terminée, s'il n'y a pas d'autres tâches dans la file d'attente, elle sera traitée immédiatement. S'il y a encore des tâches inachevées, vous devez attendre que toutes les tâches précédentes soient terminées. .être exécuté. Par conséquent, le deuxième paramètre de setTimeout est le temps de retard minimum, et non le temps d'attente.

Lorsque nous nous attendons à ce qu'une opération soit lourde et longue mais que nous ne voulons pas bloquer l'exécution du thread principal, nous utiliserons la tâche d'exécution immédiate :

setTimeout(fn, 0);
Copier après la connexion

Scénario spécial 1 : Le délai minimum est de 1 ms

Cependant, considérez comment un tel morceau de code sera exécuté :

setTimeout(()=>{console.log(5)},5)
setTimeout(()=>{console.log(4)},4)
setTimeout(()=>{console.log(3)},3)
setTimeout(()=>{console.log(2)},2)
setTimeout(()=>{console.log(1)},1)
setTimeout(()=>{console.log(0)},0)
Copier après la connexion

Après avoir compris le mécanisme de la file d'attente des événements, votre réponse devrait être 0,1,2,3,4,5, mais la réponse est 1,0,2,3,4,5, c'est parce que le mécanisme de mise en œuvre du navigateur est que l'intervalle minimum est de 1 ms.

// https://github.com/nodejs/node/blob/v8.9.4/lib/timers.js#L456

if (!(after >= 1 && after <p>Le navigateur stocke le délai sur 32 bits S'il est supérieur à <code>2^32-1 ms(24.8天)</code>, il sera exécuté immédiatement s'il provoque un débordement. </p><h3 id="strong-Scénario-spécial-Le-délai-minimum-est-de-ms-strong"><strong>Scénario spécial 2 : Le délai minimum est de 4 ms</strong></h3><p>Lorsque les appels imbriqués de la minuterie dépassent 4 niveaux, l'intervalle minimum sera de 4 ms : </p><pre class="brush:php;toolbar:false">var i=0;
function cb() {
    console.log(i, new Date().getMilliseconds());
    if (i <p> On peut voir que les 4 premières couches ne sont pas exécutées immédiatement. Après la 4ème couche, l'intervalle devient nettement supérieur à 4 ms : </p><pre class="brush:php;toolbar:false">0 667
1 669
2 670
3 672
4 676
5 681
6 685
Copier après la connexion
Les minuteries peuvent être imbriquées après cinq de ces minuteries imbriquées, cependant, l'intervalle est ; forcé d'être d'au moins quatre millisecondes.

Scénario spécial 3 : Limitation du navigateur

Afin d'optimiser les ressources occupées par le chargement des onglets en arrière-plan, le navigateur ne activer l'onglet arrière-plan Le délai du timer dans la page est limité à 1s.
Pour les scripts de suivi, tels que Google Analytics, etc., sur la page actuelle, la limite de délai est toujours de 4 ms, tandis que les onglets en arrière-plan sont de 10 s.

Tâche planifiée setInterval

À ce moment-là, nous saurons que setInterval définira une nouvelle minuterie après l'expiration du délai de chaque minuterie. . L'événement fn est placé dans la file d'attente des événements. Si la tâche précédente est exécutée trop longtemps, nous verrons des événements fn continus être exécutés sans ressentir l'intervalle de temps prédéfini.

Par conséquent, nous devrions essayer d'éviter d'utiliser setInterval et utiliser plutôt setTimeout pour simuler des tâches de synchronisation cycliques.

Fonction Sleep

JS a toujours manqué de syntaxe sleep Avec la nouvelle syntaxe d'ES6, on peut simuler cette fonction, mais la même méthode. Parce que l'utilisation de setTimeout ne peut pas garantir un délai de mise en veille précis :

function sleep(ms) {
  return new Promise(resolve => {
    setTimeout(resolve, ms);
  })
}
// 使用
async function test() {
    await sleep(3000);
}
Copier après la connexion

async await机制

async函数是Generator函数的语法糖,提供更方便的调用和语义,上面的使用可以替换为:

function* test() {
    yield sleep(3000);
}
// 使用
var g = test();
test.next();
Copier après la connexion

但是调用使用更加复杂,因此一般我们使用async函数即可。但JS时如何实现睡眠函数的呢,其实就是提供一种执行时的中间状态暂停,然后将控制权移交出去,等控制权再次交回时,从上次的断点处继续执行。因此营造了一种睡眠的假象,其实JS主线程还可以在执行其他的任务。

Generator函数调用后会返回一个内部指针,指向多个异步任务的暂停点,当调用next函数时,从上一个暂停点开始执行。

协程

协程(coroutine)是指多个线程互相协作,完成异步任务的一种多任务异步执行的解决方案。他的运行流程:

 ● 协程A开始执行

 ● 协程A执行到一半,进入暂停,执行权转移到协程B

 ● 协程B在执行一段时间后,将执行权交换给A

 ● 协程A恢复执行

可以看到这也就是Generator函数的实现方案。

宏任务和微任务

一个JS的任务可以定义为:在标准执行机制中,即将被调度执行的所有代码块。

我们上面介绍了JS如何使用单线程完成异步多任务调用,但我们知道JS的异步任务分很多种,如setTimeout定时器、Promise异步回调任务等,它们的执行优先级又一样吗?

答案是不。JS在异步任务上有更细致的划分,它分为两种:

宏任务(macrotask)包含:

 ● 执行的一段JS代码块,如控制台、script元素中包含的内容。

 ● 事件绑定的回调函数,如点击事件。

 ● 定时器创建的回调,如setTimeout和setInterval。

微任务(microtask)包含:

 ● Promise对象的thenable函数。

 ● Nodejs中的process.nextTick函数。

 ● JS专用的queueMicrotask()函数。

Compréhension approfondie du modèle de concurrence et du mécanisme de boucle dévénements de JavaScript

宏任务和微任务都有自身的事件循环机制,也拥有独立的事件队列(Event Queue),都会按照队列的顺序依次执行。但宏任务和微任务主要有两点区别:

1、宏任务执行完成,在控制权交还给主线程执行其他宏任务之前,会将微任务队列中的所有任务执行完成。

2、微任务创建的新的微任务,会在下一个宏任务执行之前被继续遍历执行,直到微任务队列为空。

浏览器的进程和线程

浏览器是多进程式的,每个页面和插件都是一个独立的进程,这样可以保证单页面崩溃或者插件崩溃不会影响到其他页面和浏览器整体的稳定运行。

它主要包括:

1、主进程:负责浏览器界面显示和管理,如前进、后退,新增、关闭,网络资源的下载和管理。

2、第三方插件进程:当启用插件时,每个插件独立一个进程。

3、GPU进程:全局唯一,用于3D图形绘制。

4、Renderer渲染进程:每个页面一个进程,互不影响,执行事件处理、脚本执行、页面渲染。

单页面线程

浏览器的单个页面就是一个进程,指的就是Renderer进程,而进程中又包含有多个线程用于处理不同的任务,主要包括:

1、GUI渲染线程:负责HTML和CSS的构建成DOM树,渲染页面,比如重绘。

2、JS引擎线程:JS内核,如Chrome的V8引擎,负责解析执行JS代码。

3、事件触发线程:如点击等事件存在绑定回调时,触发后会被放入宏任务事件队列。

4、定时触发器线程:setTimeout和setInterval的定时计数器,在时间到达后放入宏任务事件队列。

5、异步HTTP请求线程:XMLHTTPRequest请求后新开一个线程,等待状态改变后,如果存在回调函数,就将其放入宏任务队列。

需要注意的是,GUI渲染进程和JS引擎进程互斥,两者只会同时执行一个。主要的原因是为了节流,因为JS的执行会可能多次改变页面,页面的改变也会多次调用JS,如resize。因此浏览器采用的策略是交替执行,每个宏任务执行完成后,执行GUI渲染,然后执行下一个宏任务。

Webworker线程

因为JS只有一个引擎线程,同时和GUI渲染线程互斥,因此在繁重任务执行时会导致页面卡住,所以在HTML5中支持了Webworker,它用于向浏览器申请一个新的子线程执行任务,并通过postMessage API来和worker线程通信。所以我们在繁重任务执行时,可以选择新开一个Worker线程来执行,并在执行结束后通信给主线程,这样不会影响页面的正常渲染和使用。

总结

1、JS是单线程、阻塞式执行语言。

2、JS通过事件循环机制来完成异步任务并发执行。

3、JS将任务细分为宏任务和微任务来提供执行优先级。

4、浏览器单页面为一个进程,包含的JS引擎线程和GUI渲染线程互斥,可以通过新开Web Worker线程来完成繁重的计算任务。

Compréhension approfondie du modèle de concurrence et du mécanisme de boucle dévénements de JavaScript

最后给大家出一个考题,可以猜下执行的输出结果来验证学习成果:

function sleep(ms) {
    console.log('before first microtask init');
    new Promise(resolve => {
        console.log('first microtask');
        resolve()
    })
    .then(() => {console.log('finish first microtask')});
    console.log('after first microtask init');
    return new Promise(resolve => {
          console.log('second microtask');
        setTimeout(resolve, ms);
    });
}
setTimeout(async () => {
    console.log('start task');
    await sleep(3000);
    console.log('end task');
}, 0);
setTimeout(() => console.log('add event'), 0);
console.log('main thread');
Copier après la connexion

输出为:

main thread
start task
before first microtask init
first microtask
after first microtask init
second microtask
finish first microtask
add event
end task
Copier après la connexion

本文来自 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 de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn

Outils d'IA chauds

Undresser.AI Undress

Undresser.AI Undress

Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover

AI Clothes Remover

Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool

Undress AI Tool

Images de déshabillage gratuites

Clothoff.io

Clothoff.io

Dissolvant de vêtements AI

AI Hentai Generator

AI Hentai Generator

Générez AI Hentai gratuitement.

Outils chauds

Bloc-notes++7.3.1

Bloc-notes++7.3.1

Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise

SublimeText3 version chinoise

Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1

Envoyer Studio 13.0.1

Puissant environnement de développement intégré PHP

Dreamweaver CS6

Dreamweaver CS6

Outils de développement Web visuel

SublimeText3 version Mac

SublimeText3 version Mac

Logiciel d'édition de code au niveau de Dieu (SublimeText3)

La relation parent-enfant entre les fonctions golang et goroutine La relation parent-enfant entre les fonctions golang et goroutine Apr 25, 2024 pm 12:57 PM

Il existe une relation parent-enfant entre les fonctions et les goroutines dans Go. La goroutine parent crée la goroutine enfant, et la goroutine enfant peut accéder aux variables de la goroutine parent mais pas l'inverse. Créez une goroutine enfant à l'aide du mot-clé go, et la goroutine enfant est exécutée via une fonction anonyme ou une fonction nommée. La goroutine parent peut attendre que la goroutine enfant se termine via sync.WaitGroup pour s'assurer que le programme ne se termine pas avant que toutes les goroutines enfants ne soient terminées.

Application de la concurrence et des coroutines dans la conception de l'API Golang Application de la concurrence et des coroutines dans la conception de l'API Golang May 07, 2024 pm 06:51 PM

La concurrence et les coroutines sont utilisées dans la conception GoAPI pour : Traitement hautes performances : traiter plusieurs requêtes simultanément pour améliorer les performances. Traitement asynchrone : utilisez des coroutines pour traiter des tâches (telles que l'envoi d'e-mails) de manière asynchrone, libérant ainsi le thread principal. Traitement des flux : utilisez des coroutines pour traiter efficacement les flux de données (tels que les lectures de bases de données).

La relation entre la coroutine Golang et la goroutine La relation entre la coroutine Golang et la goroutine Apr 15, 2024 am 10:42 AM

Coroutine est un concept abstrait permettant d'exécuter des tâches simultanément, et goroutine est une fonction de thread légère dans le langage Go qui implémente le concept de coroutine. Les deux sont étroitement liés, mais la consommation des ressources des goroutines est moindre et gérée par le planificateur Go. Goroutine est largement utilisé dans les combats réels, comme le traitement simultané des requêtes Web et l'amélioration des performances des programmes.

Programmation simultanée C++ : comment éviter la famine des threads et l'inversion des priorités ? Programmation simultanée C++ : comment éviter la famine des threads et l'inversion des priorités ? May 06, 2024 pm 05:27 PM

Pour éviter la pénurie de threads, vous pouvez utiliser des verrous équitables pour garantir une allocation équitable des ressources ou définir des priorités de thread. Pour résoudre l'inversion de priorité, vous pouvez utiliser l'héritage de priorité, qui augmente temporairement la priorité du thread contenant la ressource ; ou utiliser la promotion de verrouillage, qui augmente la priorité du thread qui nécessite la ressource.

Comment contrôler le cycle de vie des coroutines Golang ? Comment contrôler le cycle de vie des coroutines Golang ? May 31, 2024 pm 06:05 PM

Le cycle de vie de la coroutine Go peut être contrôlé des manières suivantes : Créer une coroutine : utilisez le mot-clé go pour démarrer une nouvelle tâche. Terminez les coroutines : attendez que toutes les coroutines soient terminées, utilisez sync.WaitGroup. Utilisez les signaux de fermeture de canal. Utilisez le contexte context.Context.

Programmation simultanée C++ : comment terminer et annuler un thread ? Programmation simultanée C++ : comment terminer et annuler un thread ? May 06, 2024 pm 02:12 PM

Les mécanismes de terminaison et d'annulation de thread en C++ incluent : Terminaison de thread : std::thread::join() bloque le thread actuel jusqu'à ce que le thread cible termine son exécution ; std::thread::detach() détache le thread cible de la gestion des threads. Annulation de thread : std::thread::request_termination() demande au thread cible de terminer l'exécution ; std::thread::get_id() obtient l'ID du thread cible et peut être utilisé avec std::terminate() pour terminer immédiatement la cible. fil de discussion. En combat réel, request_termination() permet au thread de décider du moment de la fin, et join() garantit que sur la ligne principale

Guide avancé de Python asyncio : du débutant à l'expert Guide avancé de Python asyncio : du débutant à l'expert Mar 04, 2024 am 09:43 AM

Programmation simultanée et asynchrone La programmation simultanée traite de plusieurs tâches s'exécutant simultanément, la programmation asynchrone est un type de programmation simultanée dans laquelle les tâches ne bloquent pas les threads. asyncio est une bibliothèque de programmation asynchrone en python, qui permet aux programmes d'effectuer des opérations d'E/S sans bloquer le thread principal. Boucle d'événements Le cœur d'asyncio est la boucle d'événements, qui surveille les événements d'E/S et planifie les tâches correspondantes. Lorsqu'une coroutine est prête, la boucle d'événements l'exécute jusqu'à ce qu'elle attende les opérations d'E/S. Il met ensuite la coroutine en pause et continue d'exécuter d'autres coroutines. Coroutines Les coroutines sont des fonctions qui peuvent suspendre et reprendre l'exécution. Le mot-clé asyncdef est utilisé pour créer des coroutines. La coroutine utilise le mot-clé wait pour attendre la fin de l'opération d'E/S. Les bases suivantes d'asyncio

Analyse de la différence entre les threads et les processus en langage Go Analyse de la différence entre les threads et les processus en langage Go Apr 03, 2024 pm 01:39 PM

Processus et threads en langage Go : Processus : une instance de programme exécutée de manière indépendante avec ses propres ressources et espace d'adressage. Thread : unité d'exécution au sein d'un processus qui partage les ressources du processus et l'espace d'adressage. Caractéristiques : Processus : frais généraux élevés, bonne isolation, planification indépendante. Threads : faible surcharge, ressources partagées, planification interne. Cas pratique : Processus : Isoler les tâches de longue durée. Threads : traitent simultanément de grandes quantités de données.

See all articles