JavaScript
Es un lenguaje de subproceso único, el llamado subproceso único
Js mecanismo asincrónico
JavaScript
Es un lenguaje de subproceso único. El llamado subproceso único significa que solo se puede completar una tarea a la vez. Si hay varias tareas, se debe poner en cola. La tarea anterior se completa y luego se ejecuta la última tarea, y así sucesivamente. La ventaja de este modo es que es relativamente simple de implementar, y el entorno de ejecución es relativamente simple. La desventaja es que, mientras haya una tarea que lleve mucho tiempo, las tareas posteriores deben ponerse en cola y retrasarán la ejecución de todo el programa. Los navegadores comunes no responden, lo cual es un estado de falsa muerte, a menudo porque un determinado fragmento de Javascript
código se ejecuta durante mucho tiempo, como un bucle infinito, lo que hace que toda la página se atasque en este lugar y no se puedan ejecutar otras tareas.
Mecanismo de ejecución #
Para resolver los problemas anteriores, Javascript
el modo de ejecución de la tarea se divide en dos tipos: sincrónico Synchronous
y asincrónico Asynchronous
, sincrónico o asincrónico, lo que indica si todo el proceso debe completarse en secuencia, bloqueando o no bloqueando, lo que significa que la función que llama no Decirle el resultado de inmediato.
Sincronización #
El modo sincrónico es el bloqueo sincrónico. La última tarea espera el final de la tarea anterior y luego la ejecuta. El orden de ejecución del programa es coherente con el orden de las tareas.
var i = 100;
while(--i) { console.log(i); }
console.log("while 执行完毕我才能执行");
# Asincrónico
La ejecución asincrónica es la ejecución en modo sin bloqueo. Cada tarea tiene una o más funciones de devolución de llamada callback
. Después de que finaliza la tarea anterior, en lugar de ejecutar la siguiente tarea, se ejecuta la función de devolución de llamada. Esta última tarea se ejecuta sin esperar el final de la tarea anterior. Por lo tanto, el orden de ejecución del programa y el orden de las tareas son inconsistentes y asíncronos. El navegador Tab
solo asigna un Js
hilo para cada uno , la tarea principal es interactuar con el usuario y operar, DOM
etc., lo que determina que solo puede ser un solo hilo, de lo contrario traerá problemas de sincronización muy complicados, por ejemplo, suponga que JavaScript
hay dos hilos , Un subproceso DOM
agrega contenido en un determinado nodo, otro subproceso elimina el nodo y el navegador no puede determinar qué operación prevalecerá.
setTimeout(() => console.log("我后执行"), 0); // 注意:W3C在HTML标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms,此外这与浏览器设定、主线程以及任务队列也有关系,执行时间可能大于4ms,例如老版本的浏览器都将最短间隔设为10毫秒。另外,对于那些DOM的变动尤其是涉及页面重新渲染的部分,通常不会立即执行,而是每16毫秒执行一次。这时使用requestAnimationFrame()的效果要好于setTimeout()。 console.log("我先执行");
Mecanismo asincrónico #
Primero veamos un ejemplo, para probar una operación ejecutada asincrónicamente como se indica arriba
setTimeout(() => console.log("我在很长时间之后才执行"), 0); var i = 3000000000; while(--i) { } console.log("循环执行完毕");
Pruebas locales, el conjunto de setTimeout
funciones de devolución de llamada 30s
se ejecuta después de aproximadamente , que es mucho mayor que 4ms
eso. Configuré un bucle muy grande en el Js
hilo principal para bloquear el hilo principal. Tenga en cuenta que no configuré un bucle infinito. Si configuro aquí un bucle infinito Para bloquear el subproceso principal, la setTimeout
función de devolución de llamada nunca se ejecutará. Además, debido a que el subproceso de representación y el subproceso del JS
motor son mutuamente exclusivos, el Js
subproceso de representación se suspenderá mientras el subproceso está procesando la tarea, toda la página se bloqueará y no se puede actualizar o incluso No se puede cerrar, y la página solo se puede cerrar utilizando el administrador de tareas para finalizar el Tab
proceso. Js
Asíncrono se logra a través de una pila de ejecución y una cola de tareas para completar operaciones asíncronas. Todas las tareas síncronas se ejecutan en el hilo principal para formar una pila de ejecución. Varias devoluciones de eventos (también llamados mensajes) se almacenan en la cola de tareas. Una vez que se completa el procesamiento de la tarea en la pila de ejecución, el subproceso principal comienza a leer las tareas en la cola de tareas y las ejecuta, correspondiendo continuamente al bucle.
Por ejemplo, la setTimeout
devolución de llamada de evento completada en el ejemplo anterior se almacena en la cola de tareas. Debe tenerse en cuenta que el JavaScript
motor no cuenta el contador del temporizador del navegador , porque el JavaScript
motor tiene un solo subproceso. Si el subproceso está bloqueado, afectará el tiempo. Precisamente, el recuento se cuenta por el subproceso del navegador. Cuando se completa el recuento, la devolución de llamada del evento se agrega a la cola de tareas. Del mismo modo HTTP
, hay un subproceso separado en el navegador, y la devolución de llamada del evento se coloca en la cola de tareas después de la ejecución. A través de este proceso, podemos explicar por qué setTimeout
la devolución de llamada en el ejemplo anterior no se pudo ejecutar, porque el hilo principal, es decir, el código en la pila de ejecución no se ha completado, no leerá la devolución de llamada del evento en la cola de tareas para ejecutar, incluso si esta devolución de llamada de evento ya se ha ejecutado. En la cola de tareas.
Evento Loop #
El hilo principal se lee de la cola de tareas caso, el proceso se circula constantemente, por lo que también se conoce toda la operación de este mecanismo Event Loop
, Event Loop
es un modelo ejecutivo, implementado de manera diferente en diferentes lugares, navegador y NodeJS
implemento basados en diferentes tecnologías su propio Event Loop
. El navegador está claramente definido Event Loop
en HTML5
la especificación NodeJS
y Event Loop
se basa en la libuv
implementación.
Se Event Loop
compone de pila de ejecución Execution Stack
, subproceso de fondo Background Threads
, macro cola Macrotask Queue
y micro cola en el navegador Microtask Queue
.
- La pila de ejecución es una estructura de datos que ejecuta tareas de sincronización en el subproceso principal. La llamada a la función forma una pila que consta de varias tramas.
- Lograr subproceso en segundo plano es el navegador para
setTimeout
,setInterval
,XMLHttpRequest
y así sucesivamente hilo de ejecución. - cola de macro, algunas de las tareas asíncronas se convertirá en la cola de devolución de llamada macro, a la espera de ser llamado de seguimiento, incluyendo
setTimeout
,setInterval
,setImmediate(Node)
,requestAnimationFrame
,UI rendering
,I/O
y otras operaciones - Microqueue, otras tareas asíncronas se convertirá en la cola de micro devolución de llamada, a la espera de las llamadas de seguimiento, incluyendo
Promise
,process.nextTick(Node)
,Object.observe
,MutationObserver
y otras operaciones
Cuando se Js
ejecute, proceda de la siguiente manera
- Primero, el código en la pila de ejecución se ejecuta sincrónicamente, y las tareas asincrónicas en estos códigos se agregan al hilo de fondo
- Después de ejecutar el código de sincronización en la pila de ejecución, la pila de ejecución se vacía y se escanea la microcola
- Tome la primera tarea de la micro cola y colóquela en la pila de ejecución para su ejecución. En este momento, la micro cola está en cola
- Cuando se complete la pila de ejecución, continúe eliminando y ejecutando las tareas de micro cola hasta que todas las tareas de micro cola estén ejecutadas
- Después de que la última tarea de microcola se elimine y entre en la pila de ejecución, la tarea en la micro cola estará vacía. Cuando se complete la tarea de la pila de ejecución, la micro cola comenzará a escanear la superficie. Continúe escaneando la tarea de macro cola. Ejecución en el medio, una vez completada la ejecución, continúe escaneando la micro cola que está vacía, luego escanee la macro cola, elimine la ejecución
- Sigue yendo y viniendo ...
Ejemplo #
// Step 1
console.log(1);
// Step 2
setTimeout(() => { console.log(2); Promise.resolve().then(() => { console.log(3); }); }, 0); // Step 3 new Promise((resolve, reject) => { console.log(4); resolve(); }).then(() => { console.log(5); }) // Step 4 setTimeout(() => { console.log(6); }, 0); // Step 5 console.log(7); // Step N // ... // Result /* 1 4 7 5 2 3 6 */
Paso 1
// 执行栈 console
// 微队列 []
// 宏队列 []
console.log(1); // 1
Paso 2
// 执行栈 setTimeout
// 微队列 []
// 宏队列 [setTimeout1]
setTimeout(() => { console.log(2); Promise.resolve().then(() => { console.log(3); }); }, 0);
Paso 3
// 执行栈 Promise
// 微队列 [then1]
// 宏队列 [setTimeout1]
new Promise((resolve, reject) => { console.log(4); // 4 // Promise是个函数对象,此处是同步执行的 // 执行栈 Promise console resolve(); }).then(() => { console.log(5); })
Paso 4
// 执行栈 setTimeout
// 微队列 [then1]
// 宏队列 [setTimeout1 setTimeout2]
setTimeout(() => { console.log(6); }, 0);
Paso 5
// 执行栈 console
// 微队列 [then1]
// 宏队列 [setTimeout1 setTimeout2]
console.log(7); // 7
Paso 6
// 执行栈 then1
// 微队列 []
// 宏队列 [setTimeout1 setTimeout2]
console.log(5); // 5
Paso 7
// 执行栈 setTimeout1
// 微队列 [then2]
// 宏队列 [setTimeout2]
console.log(2); // 2 Promise.resolve().then(() => { console.log(3); });
Paso 8
// 执行栈 then2
// 微队列 []
// 宏队列 [setTimeout2]
console.log(3); // 3
Paso 9
// 执行栈 setTimeout2
// 微队列 []
// 宏队列 []
console.log(6); // 6
Referencia #
https://www.jianshu.com/p/1a35857c78e5
https://segmentfault.com/a/1190000016278115
https://segmentfault.com/a/1190000012925872 https://www.cnblogs.com/sunidol/p/11301808.html http://www.ruanyifeng.com/blog/2014/10/event-loop.html https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/EventLoop