Este artículo es una comprensión personal de nodejs en el desarrollo y el aprendizaje reales. Ahora está compilado para referencia futura. Sería un honor para usted si pudiera inspirarlo.
E/S : Entrada/Salida, la entrada y salida de un sistema.
Un sistema puede entenderse como un individuo, como una persona. Cuando hablas, es la salida y cuando escuchas, es la entrada.
La diferencia entre E/S con bloqueo y E/S sin bloqueo radica en si el sistema puede recibir otras entradas durante el período desde la entrada hasta la salida .
Aquí hay dos ejemplos para ilustrar qué son las E/S con bloqueo y las E/S sin bloqueo:
1. Cocinar
En primer lugar, debemos determinar el alcance de un sistema. En este ejemplo, la tía de la cantina y el camarero del restaurante se consideran un sistema.
Luego, si puede aceptar los pedidos de otras personas entre ordenar y servir comida, puede determinar si se trata de E/S con bloqueo o sin bloqueo.
En cuanto a la tía de la cafetería, no puede hacer pedidos para otros estudiantes cuando realiza el pedido. Solo después de que el estudiante haya terminado de ordenar y servido los platos, puede aceptar el pedido del siguiente estudiante, por lo que la tía de la cafetería está bloqueando la E/S.
Para un camarero de restaurante, puede servir al siguiente huésped después de realizar el pedido y antes de que el huésped sirva el plato, por lo que el camarero tiene E/S sin bloqueo.
2. Hacer las tareas del hogar
Al lavar la ropa, no es necesario esperar a que llegue la lavadora. En este momento, puede barrer el piso y ordenar el escritorio. Después de ordenar el escritorio, la ropa se lava y se puede colgar para que se seque. 25 minutos en total.
La lavandería es en realidad una E/S sin bloqueo. Puedes hacer otras cosas entre poner la ropa en la lavadora y terminar de lavar.
La razón por la que la E/S sin bloqueo puede mejorar el rendimiento es que puede ahorrar esperas innecesarias.
La clave para comprender las E/S sin bloqueo es
¿Cómo se refleja la E/S sin bloqueo de nodejs? Como se mencionó anteriormente, un punto importante para comprender la E/S sin bloqueo es determinar primero un límite del sistema. El límite del sistema del nodo es el hilo principal .
Si el siguiente diagrama de arquitectura se divide según el mantenimiento del subproceso, la línea de puntos de la izquierda es el subproceso de nodejs y la línea de puntos de la derecha es el subproceso de C++.
Ahora el hilo de Nodejs necesita consultar la base de datos. Esta es una operación de E/S típica. No esperará los resultados de la E/S y continuará procesando otras operaciones. Distribuirá una gran cantidad de potencia informática a otros C++. Hilos para cálculos.
Espere hasta que salga el resultado y devuélvalo al subproceso de nodejs. Antes de obtener el resultado, el subproceso de nodejs también puede realizar otras operaciones de E/S, por lo que no es bloqueante.
El hilo de nodejs equivale a que la parte izquierda sea el camarero y el hilo de c++ es el chef.
Por lo tanto, la E/S sin bloqueo del nodo se completa llamando a los subprocesos de trabajo de C++.
Entonces, ¿cómo notificar al hilo de Nodejs cuando el hilo de C++ obtiene el resultado? La respuesta está impulsada por eventos .
Bloqueo: el proceso duerme durante la E/S y espera a que se complete la E/S antes de continuar con el siguiente paso.
Sin bloqueo : la función regresa inmediatamente durante la E/S y el proceso no espera a la E/S; O para completar.
Entonces, para saber el resultado devuelto, es necesario utilizar el controlador de eventos .
El llamado evento impulsado por eventos puede entenderse como lo mismo que el evento de clic frontal. Primero escribo un evento de clic, pero no sé cuándo se activará. Solo cuando se activa, el hilo principal lo hará. ejecutar la función controlada por eventos.
Este modo también es un modo de observador, es decir, primero escucho el evento y luego lo ejecuto cuando se activa.
Entonces, ¿cómo implementar el impulso de eventos? La respuesta es la programación asincrónica .
Como se mencionó anteriormente, nodejs tiene una gran cantidad de E/S sin bloqueo, por lo que los resultados de las E/S sin bloqueo deben obtenerse mediante funciones de devolución de llamada. Este método de utilizar funciones de devolución de llamada es programación asincrónica . Por ejemplo, el siguiente código obtiene el resultado mediante la función de devolución de llamada:
glob(__dirname+'/**/*', (err, res) => { resultado = res console.log('obtener resultado') })
El primer parámetro de la función de devolución de llamada de nodejs es error y los parámetros siguientes son el resultado . ¿Por qué hacer esto?
intentar { entrevista(función() { console.log('sonrisa') }) } atrapar(errar) { console.log('llorar', errar) } entrevista de función (devolución de llamada) { setTimeout(() => { si(Math.random() < 0.1) { devolución de llamada ('éxito') } demás { lanzar un nuevo error ('fallar') } }, 500) }
Después de la ejecución, no se detectó y el error se arrojó globalmente, lo que provocó que todo el programa nodejs fallara.
Try Catch no lo detecta porque setTimeout vuelve a abrir el bucle de eventos. Cada vez que se abre un bucle de eventos, se regenera un contexto de pila de llamadas que pertenece a la pila de llamadas del bucle de eventos anterior. el contexto de la pila de llamadas Todo es diferente. No hay intento de captura en esta nueva pila de llamadas, por lo que el error se genera globalmente y no se puede detectar. Para obtener más información, consulte este artículo. Problemas al realizar try catch en una cola asincrónica.
Entonces, ¿qué hacer? Pase el error como parámetro:
entrevista de función (devolución de llamada) { setTimeout(() => { si(Math.random() < 0.5) { devolución de llamada ('éxito') } demás { devolución de llamada (nuevo error ('fallo')) } }, 500) } entrevista(función (res) { si (res instancia de error) { console.log('llorar') devolver } console.log('sonrisa') })
Pero esto es más problemático y debe realizar un juicio en la devolución de llamada, por lo que existe una regla madura. El primer parámetro es err. Si no existe, significa que la ejecución fue exitosa.
entrevista de función (devolución de llamada) { setTimeout(() => { si(Math.random() < 0.5) { devolución de llamada (nula, 'éxito') } demás { devolución de llamada (nuevo error ('fallo')) } }, 500) } entrevista(función (res) { si (res) { devolver } console.log('sonrisa') })El método de escritura de devolución de llamada de los nodejs de
no solo provocará el área de devolución de llamada, sino que también provocará el problema del control de procesos asincrónicos .
El control de procesos asincrónicos se refiere principalmente a cómo manejar la lógica de concurrencia cuando ocurre la concurrencia. Siguiendo con el ejemplo anterior, si su colega entrevista a dos empresas, no será entrevistado por la tercera empresa hasta que entreviste con éxito a dos empresas. Entonces, ¿cómo escribir esta lógica? Necesita agregar un recuento de variables globalmente:
var count = 0 entrevista((err) => { si (errar) { devolver } contar++ si (cuenta >= 2) { // Lógica de procesamiento} }) entrevista((err) => { si (errar) { devolver } contar++ si (cuenta >= 2) { // Lógica de procesamiento} })
Escribir como el anterior es muy problemático y feo. Por lo tanto, los métodos de escritura de promesa y async/await aparecieron más tarde.
que el bucle de eventos actual no puede obtener el resultado, pero el bucle de eventos futuro le dará el resultado. Es muy parecido a lo que diría un cabrón.
La promesa no es solo una basura, sino también una máquina de estados:
const pro = new Promise((resolver, rechazar) => { setTimeout(() => { resolver('2') }, 200) }) console.log(pro) // Imprimir: Promesa { <pending> }
La ejecución de then o catch devolverá una nueva promesa . El estado final de la promesa está determinado por los resultados de la ejecución de las funciones de devolución de llamada de then y catch:
entrevista de función() { devolver nueva Promesa((resolver, rechazar) => { setTimeout(() => { si (Math.random() > 0.5) { resolver('éxito') } demás { rechazar (nuevo error ('fallar')) } }) }) } var promesa = entrevista() var promesa1 = promesa.entonces(() => { devolver nueva Promesa((resolver, rechazar) => { setTimeout(() => { resolver('aceptar') }, 400) }) })
El estado de la promesa1 está determinado por el estado de la promesa a cambio, es decir, el estado de la promesa1 después de que se ejecuta la promesa a cambio. ¿Cuáles son los beneficios de esto? Esto resuelve el problema del infierno de devolución de llamadas .
var promesa = entrevista() .entonces(() => { entrevista de regreso () }) .entonces(() => { entrevista de regreso () }) .entonces(() => { entrevista de regreso () }) .catch(e => { consola.log(e) })
Luego, si el estado de la promesa devuelta es rechazado, se llamará al primer catch y no se llamará al siguiente. Recuerde: las llamadas rechazadas son la primera captura y las llamadas resueltas son las primeras.
Si la promesa es solo para resolver devoluciones de llamadas infernales, es demasiado pequeña para subestimar la promesa. La función principal de la promesa es resolver el problema de control de procesos asincrónicos. Si desea entrevistar a dos empresas al mismo tiempo:
función entrevista() { devolver nueva Promesa((resolver, rechazar) => { setTimeout(() => { si (Math.random() > 0.5) { resolver('éxito') } demás { rechazar (nuevo error ('fallar')) } }) }) } promesa .all([entrevista(), entrevista()]) .entonces(() => { console.log('sonrisa') }) // Si una empresa rechaza, atrápala .catch(() => { console.log('llorar') })
¿Qué es exactamente sync/await?
console.log(async function() { volver 4 }) consola.log(función() { devolver nueva Promesa((resolver, rechazar) => { resolver(4) }) })
El resultado impreso es el mismo, es decir, async/await es solo azúcar sintáctico para promesa.
Sabemos que try catch captura errores según la pila de llamadas y solo puede capturar errores por encima de la pila de llamadas. Pero si usa await, puede detectar errores en todas las funciones de la pila de llamadas. Incluso si el error se genera en la pila de llamadas de otro bucle de eventos, como setTimeout.
Después de transformar el código de la entrevista, puede ver que el código se ha simplificado mucho.
intentar { esperando entrevista(1) esperando entrevista(2) esperando entrevista(2) } captura(e => { consola.log(e) })
¿Qué pasa si es una tarea paralela?
await Promise.all([interview(1), entrevista(2)])
Debido a la E/S sin bloqueo de nodejs, es necesario utilizar métodos controlados por eventos para obtener resultados de E/S Para lograr el evento. Para obtener resultados, debe utilizar la programación asincrónica, como las funciones de devolución de llamada. Entonces, ¿cómo ejecutar estas funciones de devolución de llamada para obtener los resultados? Entonces necesitas usar un bucle de eventos.
El bucle de eventos es la base clave para realizar la función de E/S sin bloqueo de nodejs. Las E/S sin bloqueo y el bucle de eventos son capacidades proporcionadas por la biblioteca libuv
de C++.
Demostración de código:
const eventloop = { cola: [], bucle() { mientras (esta.cola.longitud) { devolución de llamada constante = this.queue.shift() llamar de vuelta() } setTimeout(este.loop.bind(esto), 50) }, agregar (devolución de llamada) { esta.cola.push(devolución de llamada) } } eventloop.loop() setTimeout(() => { eventloop.add(() => { consola.log('1') }) }, 500) setTimeout(() => { eventloop.add(() => { consola.log('2') }) }, 800)
setTimeout(this.loop.bind(this), 50)
garantiza que después de 50 ms, comprobará si hay una devolución de llamada en la cola y, de ser así, la ejecutará. Esto forma un bucle de eventos.
Por supuesto, los eventos reales son mucho más complicados y hay más de una cola. Por ejemplo, hay una cola de operaciones de archivos y una cola de tiempo.
bucle de eventos constante = { cola: [], fsQueue: [], cola de temporizador: [], bucle() { mientras (esta.cola.longitud) { devolución de llamada constante = this.queue.shift() llamar de vuelta() } this.fsQueue.forEach(devolución de llamada => { si (hecho) { llamar de vuelta() } }) setTimeout(este.loop.bind(esto), 50) }, agregar (devolución de llamada) { esta.cola.push(devolución de llamada) } }
En primer lugar, entendemos qué es la E/S sin bloqueo, es decir, omitimos inmediatamente la ejecución de tareas posteriores cuando encontramos E/S y no esperaremos el resultado de la E/S. Cuando se procesa la E/S, se llamará a la función de procesamiento de eventos que registramos, que se denomina impulsada por eventos. La programación asincrónica es necesaria para implementar la gestión de eventos. La programación asincrónica es el enlace más importante en nodejs. Va desde la función de devolución de llamada hasta la promesa y finalmente a async/await (usando un método sincrónico para escribir lógica asincrónica).