Maison  >  Article  >  interface Web  >  Techniques classiques : monothreading et JavaScript asynchrone

Techniques classiques : monothreading et JavaScript asynchrone

WBOY
WBOYavant
2022-02-03 07:00:301978parcourir

Cet article vous apporte des connaissances pertinentes sur le monothreading et l'asynchronisme en JavaScript. J'espère qu'il vous sera utile.

Techniques classiques : monothreading et JavaScript asynchrone

Quand j'ai écrit cet article, j'ai aussi lu de nombreux articles, mais la plupart d'entre eux étaient très simples et les choses conceptuelles étaient très vagues, alors j'ai cherché des cours, les ai écoutés et pris quelques notes, ici. Je vais le résumer brièvement pour un examen futur ~

Processus et fils de discussion

1 : Une exécution du programme, il occupe un espace mémoire unique ---- accessible via Windows Voir le processus. dans le gestionnaire de tâches ;

2. Thread : C'est une unité d'exécution indépendante au sein du processus ; c'est un processus complet d'exécution de programme ; 3. La relation entre les processus et les threads :

* Il y a généralement au moins un thread en cours d'exécution dans un processus :

Thread principal

-- créé automatiquement après le démarrage du processus * Plusieurs threads peuvent également être exécutés simultanément ; un processus, dirons-nous Le programme est

multi-thread

 ; * Les données au sein d'un processus peuvent être directement partagées par plusieurs threads ;

* Les données entre plusieurs processus ne peuvent pas être directement partagées 4. ou plusieurs processus ?

* Certains sont des processus uniques

* firefox

* Certains sont des processus multiples

* chrome

5. Comment vérifier si le navigateur s'exécute dans plusieurs processus ?

* Gestionnaire de tâches = =>Processus

6. Le navigateur fonctionne-t-il avec un seul thread ou avec plusieurs threads ? threaded

Une caractéristique majeure du langage JavaScript est qu'il est monothread, ce qui signifie qu'il ne peut faire qu'une seule chose à la fois.

//栗子
console.log(1)
console.log(2)
console.log(3)
//输出顺序 1 2 3
2. Pourquoi JavaScript est monothread

Tout d'abord, c'est pour des raisons historiques. Lorsque le langage JavaScript a été créé, les architectures multi-processus et multi-thread n'étaient pas populaires et le support matériel n'était pas bon.

Deuxièmement, en raison de la complexité du multi-threading, les opérations multi-thread nécessitent un verrouillage, et la complexité du codage va augmenter.


La dernière chose est liée à son objectif. En tant que langage de script de navigateur, l'objectif principal de JavaScript est d'interagir avec les utilisateurs et de faire fonctionner le DOM. Si le DOM est exploité en même temps, sans multi-thread, il finira par le faire. conduire au résultat du rendu DOM imprévisible.

Afin de profiter de la puissance de calcul des CPU multicœurs, HTML5 propose le standard Web Worker, qui permet aux scripts JavaScript de créer plusieurs threads, mais les threads enfants sont entièrement contrôlés par le thread principal et ne doivent pas faire fonctionner le DOMAINE. Par conséquent, cette nouvelle norme ne modifie pas la nature monothread de JavaScript.

Synchronisation et asynchrone

1. Tâches synchrones/tâches asynchrones de JS
  • Tâches synchrones :
  • Les tâches mises en file d'attente pour exécution sur
  • le thread principal ne peuvent être exécutées qu'après
  • la
  • tâche précédente a été exécutée

Une tâche ; 步 Toutes les tâches synchrones sont effectuées sur le thread principal pour former une pile de contexte d'exécution.

Tâches asynchrones : Tâches exécutées en dehors du

thread principal

 ; Il existe également une "

file d'attente des tâches

" (file d'attente des tâches) en dehors du thread principal. Lorsque la tâche asynchrone est terminée, elle sera placée sous la forme de. une fonction de rappel en attente dans la file d'attente des tâches Lorsque le thread principal est inactif, le thread principal ira dans la file d'attente des événements pour retirer la fonction de rappel en attente et la placera dans le thread principal pour exécution. Ce processus est exécuté à plusieurs reprises pour former le mécanisme de boucle d'événement (Event Loop) de js.

//栗子
// 同步
console.log(1)

// 异步
setTimeout(()=>{
    console.log(2)
},100)

// 同步
console.log(3)

//输出顺序 1 3 2
2. Pourquoi JavaScript doit-il être asynchroneSi un certain morceau de code est exécuté trop longtemps lors de l'exécution du code JS, le code suivant ne sera pas exécuté pendant une longue période, ce qui entraînera un

blocage

(c'est-à-dire bloqué), ce qui affectera l'expérience utilisateur. 3. Comment implémenter l'asynchrone en JavaScript

1) Pile d'exécution et file d'attente des tâches En fait, nous avons déjà mentionné ci-dessus que JS implémente l'asynchrone via boucle d'événements Comprenons d'abord quelques concepts :  ;

  • JS任务 分为同步任务(synchronous)和异步任务(asynchronous)
  • 同步任务都在 JS引擎线程(主线程) 上执行,形成一个执行栈(call stack)
  • 事件触发线程 管理一个 任务队列(Task Queue)
  • 异步任务 触发条件达成,将 回调事件 放到任务队列(Task Queue)中
  • 执行栈中所有同步任务执行完毕,此时JS引擎线程空闲,系统会读取任务队列,将可运行的异步任务回调事件添加到执行栈中,开始执行

 当一个JS文件第一次执行的时候,js引擎会 解析这段代码,并将其中的同步代码 按照执行顺序加入执行栈中,然后从头开始执行。如果当前执行的是一个方法,那么js会向执行栈中添加这个方法的执行环境,然后进入这个执行环境继续执行其中的代码。当这个执行环境中的代码 执行完毕并返回结果后,js会退出这个执行环境并把这个执行环境销毁,回到上一个方法的执行环境。这个过程反复进行,直到执行栈中的代码全部执行完毕。

栗子 

//(1)
console.log(1)

//(2)
setTimeout(()=>{
    console.log(2)
},100)

//(3)
console.log(3)
  1. 先解析整段代码,按照顺序加入到执行栈中,从头开始执行
  2. 先执行(1),是同步的,所以直接打印 1
  3. 执行(2),发现是 setTimeout,于是调用浏览器的方法(webApi)执行,在 100ms后将 console.log(2) 加入到任务队列
  4. 执行(3),同步的,直接打印 3
  5. 执行栈已经清空了,现在检查任务队列,(执行太快的话可能此时任务队列还是空的,没到100ms,还没有将(2)的打印加到任务队列,于是不停的检测,直到队列中有任务),发现有 console.log(2),于是添加到执行栈,执行console.log(2),同步代码,直接打印 2 (如果这里是异步任务,同样会再走一遍循环:-->任务队列->执行栈)

所以结果是 1 3 2;

注意:setTimeout/Promise等我们称之为任务源。而进入任务队列的是他们指定的回调;

2)宏任务(macro task)与微任务(micro task)

 上面的循环只是一个宏观的表述,实际上异步任务之间也是有不同的,分为 宏任务(macro task) 与 微任务(micro task),最新的标准中,他们被称为 task与 jobs

  • 宏任务有哪些:script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering(渲染)
  • 微任务有哪些:process.nextTick, Promise, Object.observe(已废弃), MutationObserver(html5新特性)

下面我们再详细讲解一下执行过程:

 执行栈在执行的时候,会把宏任务放在一个宏任务的任务队列,把微任务放在一个微任务的任务队列,在当前执行栈为空的时候,主线程会 查看微任务队列是否有事件存在。如果微任务队列不存在,那么会去宏任务队列中 取出一个任务 加入当前执行栈;如果微任务队列存在,则会依次执行微任务队列中的所有任务,直到微任务队列为空(同样,是吧队列中的事件加到执行栈执行),然后去宏任务队列中取出最前面的一个事件加入当前执行栈...如此反复,进入循环。

注意:

  • 宏任务和微任务的任务队列都可以有多个
  • 当前执行栈执行完毕时会立刻先处理所有微任务队列中的事件,然后再去宏任务队列中取出一个事件。同一次事件循环中,微任务永远在宏任务之前执行。
  • 不同的运行环境 循环策略可能有不同,这里探讨chrome、node环境

 栗子

//(1)
setTimeout(()=>{
    console.log(1)   // 宏任务
},100)

//(2)
setTimeout(()=>{
    console.log(2)  // 宏任务
},100)

//(3)
new Promise(function(resolve,reject){
    //(4)
    console.log(3)  // 直接打印
    resolve(4)
}).then(function(val){
    //(5)
    console.log(val); // 微任务
})

//(6)
new Promise(function(resolve,reject){
    //(7)
    console.log(5)   // 直接打印
    resolve(6)
}).then(function(val){
    //(8)
    console.log(val);  // 微任务
})

//(9)
console.log(7)  // 直接打印

//(10)
setTimeout(()=>{
    console.log(8) // 宏任务,单比(1)(2)宏任务早
},50)

 上面的代码在node和chrome环境的正确打印顺序是 3 5 7 4 6 8 1 2

下面分析一下执行过程:

  1. Tous les codes sont ajoutés à la pile d'exécution après l'analyse
  2. Exécuter (1), tâche macro, appeler webapi setTimeout, cette méthode placera la fonction de rappel dans la file d'attente des tâches de la tâche macro après 100 ms
  3. Exécuter (2), la même chose que (1) ), mais il sera exécuté un peu plus tard que (1)
  4. Exécutez (3), exécutez new Promise de manière synchrone, puis exécutez (4), imprimez 3 directement, puis résolvez (4), puis . then(), put (5) Entrez dans la file d'attente des tâches de la microtâche
  5. et exécutez (6), comme ci-dessus, imprimez d'abord 5, puis exécutez solve(6), puis le contenu de .then() (8) est ajouté à la file d'attente des tâches de la microtâche
  6. Execute (9) , synchronise le code, imprime directement 7
  7. Execute (10), le même que (1) et (2), mais le temps est plus court, la console de rappel. log(8) sera ajouté à la file d'attente des tâches de la macro-tâche après 50 ms
  8. Exécuter maintenant La pile est effacée et la file d'attente des microtâches est vérifiée, et il s'avère que (5) est ajouté à la pile d'exécution pour l'exécution. Il s'agit d'un code synchrone, et 4
  9. est imprimé directement. La file d'attente des tâches a été à nouveau exécutée, et la file d'attente des microtâches est à nouveau vérifiée, et (8) est trouvé, et 6
  10. est imprimé
  11. La file d'attente des tâches a terminé son exécution. Vérifiez à nouveau la file d'attente des microtâches. S'il n'y a pas de tâche, vérifiez à nouveau la file d'attente des macrotâches. Si elle dépasse 50 ms à ce moment, vous constaterez que console.log(8) est dans la file d'attente des macrotâches, alors imprimez 8
  12. dans l'ordre. . Imprimer 1 2

Remarque : Le rendu étant également une tâche de macro, le rendu doit être exécuté après l'exécution d'une pile d'exécution. Par conséquent, s'il existe plusieurs codes qui modifient de manière synchrone le même style dans la pile d'exécution en même temps. temps, ne rend que le dernier.

Recommandations associées : Tutoriel d'apprentissage Javascript

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