datos binarios
Todo el contenido de la computadora: texto, números, imágenes, audio y video eventualmente se representarán mediante binarios.
JS
puede procesar directamente datos muy intuitivos: como cadenas, generalmente mostramos estos contenidos a los usuarios,
pero puede pensar que JS es
También puede procesar imágenes.
JS
o HTML
al navegador. Solo es responsable de decirle al navegador la dirección de la imagenSin embargo, es diferente para el servidor.
utf-8
, sino con GBK
. debemos leer sus datos binarios y luego convertirlos en el texto correspondiente a través de GKB.sharp
en Node, que se encarga de leer las imágenes o Buffer
de las imágenes entrantes y luego procesarlas.Node
, se establece una conexión larga a través de TCP
y transmite un byte. stream Necesitamos Los datos se convierten en bytes antes de pasarlos, y es necesario conocer el tamaño de los bytes transmitidos (el cliente debe juzgar cuánto contenido leer según el tamaño)Buffer y Binary
Encontraremos eso. para el desarrollo front-end, generalmente rara vez está relacionado con el procesamiento binario entre sí, pero para el lado del servidor, para implementar muchas funciones, debemos operar directamente sus datos binarios,
por lo tanto, para facilitar que los desarrolladores completen más funciones. , Node
nos proporciona una clase llamada Buffer
y es global. Como
dijimos antes, los datos binarios se almacenan en el Buffer, entonces, ¿cómo se almacenan?
8
bits: 00000000
, que es exactamente un byte.
1 byte = 8 bit
byte
.1 byte = 8 bit
, 1kb = 1024 byte
, 1M = 1024kb
, 1 G = 1024 M
int
en muchos lenguajes de programación es 4
bytes y el tipo long
es de 8
bytes.TCP
transmiteRGB
son 255
respectivamente, por lo que, en esencia,el búfer y el
Buffer
de cadena se almacenan con un byte en la computadora, que es. equivalente a una matriz de bytes. Cada elemento de la matriz tiene un tamaño de un byte.
Si queremos colocar una cadena en un búfer, ¿cuál es el proceso?
buffer
.const message = 'Hello'. // Usa la nueva palabra clave para crear una instancia de búfer, pero este método de creación ha caducado const buffer = new Buffer(message) console.log(búfer); // <Búfer 48 65 6c 6c 6f> console.log(buffer.toString()); // Hola,
codificación y decodificación de cadenas chinas.
buffer
es utf-8
, por lo que en el siguiente código, la clase Buffer
usa la codificación utf-8 para codificar nuestra cadena. , también usamos utf-8 para decodificar nuestras cadenas.3
bytesconst message = 'Hola'. // Usa Buffer.from para decodificar nuestra cadena const buffer = Buffer.from(message) console.log(búfer); // <Búfer e4 bd a0 e5 a5 bd e5 95 8a> // Hay un método toString en la instancia del búfer que puede decodificar la codificación console.log(buffer.toString()); // 'Hola'
, ¿qué pasará si la codificación y la decodificación utilizan diferentes formas de resultados de codificación?
const message = 'Hello' buffer constante = Buffer.from(mensaje, 'utf16le') console.log(búfer); // <Búfer 60 4f 7d 59 4a 55> console.log(buffer.toString()); // `O}YJU
Otras formas de crear buffers
Hay muchas formas de crear buffer
. Aquí podemos crear Buffer
a través de alloc
Podemos crear instancias de buffer directamente en forma de matrices. cada bit se modifica.
//, lo que puede especificar nuestro número de dígitos. Por ejemplo, si se pasa 8 aquí, entonces el búfer creado tendrá 8 elementos y el número binario correspondiente a cada elemento es 0. búfer constante = Buffer.alloc(8) console.log(búfer); // <Búfer 00 00 00 00 00 00 00 00> // Si el valor se asigna a un número decimal, el buffer nos ayudará a convertirlo a un número hexadecimal y luego escribirlo en la ubicación correspondiente buffer[0] = 88 // En js, todo lo que comienza con 0x se representa como un búfer numérico hexadecimal [1] = 0x88 console.log(buffer); // <Buffer 58 88 00 00 00 00 00 00>
Operaciones de buffer y archivo
1. Si el archivo de texto
buffer
original. , que es el resultado del contenido del archivo. Número binario codificado en utf-8
const fs = require('fs') fs.readFile('./a.txt', (err, datos) => { console.log(datos); // <Búfer e5 93 88 e5 93 88> })
const fs = require('fs') // codificación indica la codificación de caracteres utilizada para la decodificación, y la codificación predeterminada es utf-8 fs.readFile('./a.txt', { codificación: 'utf-8' }, (err, datos) => { console.log(data); // Jaja})
const fs = require('fs') // La codificación usa codificación de caracteres utf16le y la decodificación usa el formato utf-8. Debe ser que la decodificación no es correcta. , datos) => { consola.log(datos); // Error }) // El código anterior es similar al siguiente código const msg = 'Jaja' búfer constante = Buffer.from(msg, 'utf-8') console.log(buffer.toString('utf16le')); //
2. El archivo de imagen
copia la codificación de la imagen para lograr el propósito de copiar la imagen,
encoding
, porque la codificación de caracteres. solo se lee al leer la imagen. Solo es útil al recuperar archivos de textoconst fs = require('fs').
fs.readFile('./logo.png', (err, datos) => { console.log(data); // Lo que se imprime es la codificación binaria correspondiente al archivo de imagen // También podemos escribir la codificación de la imagen en otro archivo, lo que equivale a copiar la imagen fs.writeFile(' ./bar .png', datos, err => { consola.log(err); }) })
sharp
const Sharp = require('sharp') // Recorta la imagen logo.png a 200x300 y cópiala en el archivo bax.png Sharp('./logo.png') .redimensionar(200, 300) .toFile('./bax.png', (err, información) => { consola.log(err); }) // También puedes convertir el archivo de imagen en un búfer primero y luego escribirlo en el archivo. También puedes copiar la imagen Sharp('./logo.png') .redimensionar(300, 300) .toBuffer() .entonces(datos => { fs.writeFile('./baa.png', datos, err => { consola.log(err); }) })
Proceso de creación de buffer
Buffer
, no solicitaremos memoria del sistema operativo con frecuencia. De forma predeterminada, primero solicitará una memoria de 8 * 1024
bytes, es decir, 8kb
¿Qué es un bucle de eventos?
¿Qué es el bucle de eventos?
JS
que escribimos y el navegador o Node
.JS
que escribimos y las llamadas a la API del navegador ( setTimeout
, AJAX
,监听事件
, etc. ) Los puentes se comunican a través de funciones de devolución de llamada.file system
, networ
, etc.).Proceso y subproceso
Proceso y subproceso son dos conceptos en el sistema operativo:
process
): el programa que la computadora ha ejecutadothread
): la unidad más pequeña que el sistema operativo puede ejecutar el programa de cálculo, por lo que CPU
puede operar directamente El hilo, quesuena muy abstracto, lo explicamos intuitivamente:
Usemos un ejemplo vívido para explicar
de desarrollo de múltiples
subprocesos (mientras escucha música y escribe código). y comprobar información) funcionan al mismo tiempo?
CPU
es muy rápida y puede cambiar rápidamente entre múltiples procesos.Navegadores y JavaScript
A menudo decimos que JavaScript
tiene un solo subproceso, pero el subproceso JS debe tener su propio proceso contenedor Node
¿el navegador o el navegador Node es un proceso?
tab
, se iniciará un nuevo proceso para evitar que una página se bloquee y haga que todas las páginas dejen de responder. Es necesario forzar a todo el navegador a salir deSin embargo, la ejecución del código JavaScript se ejecuta en un subproceso separado.
JS
mismo tiempo.del proceso de ejecución de JavaScript
no se ejecutará hasta que se inserte en la pila de llamadas de función. Analicemos el proceso de ejecución del código
const message = 'Hello World'. consola.log(mensaje); función suma(núm1, núm2) { devolver número1 + número2 } función foo() { resultado constante = suma (20, 30) consola.log(resultado); } foo()
main
como otros lenguajes de programaciónmessage
variablelog
. La función se colocará Ingrese a la pila de llamadas de función. Después de la ejecución, saque la pilafoo
. La función foo se inserta en la pila de llamadas de función. Sin embargo, la función sum
debe llamarse durante la ejecución,js
y la función principal sale delbucle de eventos del navegador
. ¿Qué sucede si hay operaciones asincrónicas durante la ejecución del código JS
?
setTimeout
en el medioLuego, la función se pasa a la función setTimeout. (la llamamos función timer
), ¿cuándo se ejecutará?
web api
. El navegador almacenará la función de devolución de llamada por adelantado. En el momento apropiado, la función del temporizador se agregará a una cola de eventos¿Por qué setTimeout no bloquea la ejecución del código
Esto se debe a que el navegador mantiene algo muy, muy importante: el
navegador de bucle de eventos nos ayudará a guardar la función de devolución de llamada en setTimeout de alguna manera. El método más común es guardarlo en un árbol rojo-negro
y esperar hasta que se programe setTimeout. Cuando llegue la hora del temporizador, sacará nuestra función de devolución de llamada del temporizador del lugar guardado y la colocará en la cola de eventos.
Una vez que el bucle de eventos encuentre que hay algo en nuestra cola y la pila de llamadas de función actual está vacía, otra. Después de ejecutar el código de sincronización, las funciones de devolución de llamada en nuestra cola se retirarán de la cola y se colocarán en la pila de llamadas de funciones para su ejecución (la siguiente función no se insertará en la pila hasta que salga la función anterior en la cola,
por supuesto
)., no debe haber solo un evento. Por ejemplo, durante un determinado proceso, el usuario hace clic en un botón en el navegador. Es posible que tengamos un monitor para hacer clic en este botón, que corresponde a una función de devolución de llamada. también se agregará a nuestro En la cola, el orden de ejecución se basa en el orden en que se encuentran en la cola de eventos. También hay un resumen de las devoluciones de llamada que enviamos solicitudes ajax
a la cola de eventos
: De hecho, el bucle de eventos es algo muy simple, lo que significa que cuando es necesario ejecutar una determinada devolución de llamada en una situación especial, la devolución de llamada se guardará. de antemano se coloca en la cola de eventos, y el bucle de eventos lo saca y lo coloca en la pila de llamadas de función.
Tareas macro y microtareas
Sin embargo, el bucle de eventos no mantiene solo una cola. De hecho, hay dos colas y la ejecución de las tareas en la cola debe esperar
macrotask queue
ajax
setTimeout
, setInterval
, monitoreo DOM
, UI Rendering
y otrasmicrotask queue
): then
de llamada de Promise
, Mutation Observer API
, queueMicrotask()
, etc.Entonces, ¿cuál es la prioridad de las dos colas en el bucle de eventos?
main script
se ejecuta primero (el código del script de nivel superior escrito).decir
Puntos de prueba: main stcipt
, setTimeout
, Promise
, then
, queueMicrotask
setTimeout(() => { console.log('set1');4 nueva promesa (resolver => { resolver() }).entonces(resolver => { nueva promesa (resolver => { resolver() }).entonces(() => { console.log('entonces4'); }) console.log('entonces2'); }) }) nueva promesa (resolver => { console.log('pr1'); resolver() }).entonces(() => { console.log('entonces1'); }) setTimeout(() => { console.log('set2'); }) consola.log(2); colaMicrotask(() => { console.log('queueMicrotask'); }) nueva promesa (resolver => { resolver() }).entonces(() => { console.log('entonces3'); }) // pr1 // 2 //entonces1 //colaMicrotarea //entonces3 // conjunto1 //entonces2 //entonces4 // set2
setTimeout
se insertará en la pila de llamadas de función inmediatamente y se extraerá de la pila inmediatamente después de la ejecución. Su función timer
se colocará en la cola de tareas macro.
La función pasada a la clase Promise
se ejecutará inmediatamente. No es una función de devolución de llamada, por lo que se imprimirá pr1
y, debido a que se ejecuta el método resolve
, el estado de la Promesa cambiará inmediatamente a fulfilled
, de modo que cuando se ejecute la función then
, se ejecutará su función de devolución de llamada correspondiente. se colocará en la cola de microtareas y
se encontrará nuevamente una función setTimeout Cuando se extraiga la pila, su función de temporizador se colocará en la cola de tareas macro
cuando encuentre console.log
Después de que la función se inserte en la pila, 2
. Se imprime y luego aparece
Una función está vinculada a queueMicrotask
aquí, y la función se colocará Después de ingresar a la cola de microtask,
se encontró una nueva declaración de promesa, pero inmediatamente cambió el estado de la promesa a cumplido, por lo que la devolución de llamada. La función correspondiente a entonces también se colocó en la cola de microtarea.
Dado que se ejecutó el código del script de sincronización, ahora el evento Al comienzo del ciclo, las tareas que compiten con la cola de microtarea y la macrotarea se colocan en la cola de microtarea. pila de llamadas de funciones en orden de prioridad Nota: La prioridad de las microtareas es mayor que la de las macrotareas. Debe leerla cada vez que desee ejecutar una macrotarea. Compruebe si la cola de microtareas está vacía. no está vacío, primero debe ejecutar la tarea de la cola de microtarea.
La primera microtarea es imprimir then1
, la segunda microtarea es imprimir colaMicrotarea y la tercera microtarea es imprimir then3
. Después de eso, comience a ejecutar la macrotarea.
La primera macrotarea es más complicada. Primero imprimirá set1
y luego ejecutará una new promise
que cambiará inmediatamente el estado. Luego, la devolución de llamada se colocará en la cola de microtarea. La cola no está vacía, por lo que se debe ejecutar una cola de microtareas con una prioridad más alta, lo que equivale a que la devolución de llamada se ejecute inmediatamente. Es la misma nueva declaración de Promesa, y su intercambio correspondiente se coloca en la cola de microtask. Tenga en cuenta que hay una función console
después de la nueva declaración de Promesa. Esta función se ejecutará inmediatamente después de que se ejecute la nueva declaración de Promesa, es decir, imprimir then2
. Todavía hay una tarea en la confrontación de microtask, por lo que el siguiente paso es imprimir. then4
. Hasta ahora, la cola de microtask está vacía y la cola de macrotask puede continuar ejecutándose
, por lo que se imprimirá el siguiente set2
de macrotask2. Después de ejecutar la macrotarea,
el resultado de la impresión de todo el código es: pr1 -> 2 -> then1 -> queueMicrotask -> then3 -> set1 -> then2 -> then4 -> set2
Preguntas de la entrevista <2>
Puntos de prueba: main script
, setTimeout
, Promise
, then
, queueMicrotask
, await
async
suplemento de conocimiento asíncrono: async, await es un azúcar de sintaxis para Promise
Cuando se trata de problemas de bucle de eventos,
new Promise((resolve,rejcet) => { 函数执行})
then(res => {函数执行})
en la función asíncrona de Promesa anteriorasync1() { console.log('inicio async1'); esperar async2() console.log('final async1'); } función asíncrona async2() { console.log('async2'); } console.log('inicio del script'); setTimeout(() => { console.log('setTimeout'); }, 0) asíncrono1() nueva promesa (resolver => { console.log('promesa1'); resolver() }).entonces(() => { console.log('promesa2'); }) console.log('fin del script'); // inicio del script // inicio asíncrono1 // asíncrono2 // promesa1 // final del guión // final asíncrono1 // promesa2 // setTimeout
es una definición de función al principio y no es necesario insertarla en la pila de llamadas de función para su ejecución hasta que encuentre la primera declaración console
. Después de presionar la pila, ejecute el script start
de impresión y luego sáquelo de la pila. apila
para encontrar la primera función setTimeout
, que corresponde a timer
se colocará en la cola de tareas macro
y se ejecutará la función async1. Primero, se imprimirá async1 start
y luego se ejecutará la función async2
después de await
. Debido a que, como se mencionó anteriormente, la función después de la palabra clave await se considera new Promise
, esta función se ejecutará inmediatamente, por lo que se imprimirá async2, pero el código después de la declaración await es equivalente a colocarse en entonces. devolución de llamada, es decir, console.log('async1 end')
Esta línea de código se coloca en la cola de microtask
y el código continúa ejecutándose. Encuentra una nueva declaración de Promesa, por lo que promise1
se imprime inmediatamente. luego la devolución de llamada se coloca en la cola de microtareas para
y
script end
bucle de eventos irá a las colas de macrotareas y microtareas para ejecutar las tareas.
cola de tareas. Se ejecutará la declaración de impresión correspondiente a la primera microtarea, lo que significa que se imprimirá async1 end
y luego se imprimirá promise2
. En este momento, la cola de microtask está vacía y las tareas en la cola de macrotask se inician.
ejecuta
y la secuencia de impresión final es: script start -> async1 start -> async2 -> promise1 -> script end -> async1 end -> promise2 -> setTimeout