Node.js Funciones de devolución de llamada
Node.js es un entorno de ejecución de JavaScript basado en el motor Chrome V8, que permite que JavaScript se ejecute en el servidor sin necesidad de un navegador.
Una de las características principales de Node.js es su modelo de E/S (entrada/salida) no bloqueante, lo que hace que Node.js sea muy adecuado para manejar aplicaciones de red de alta concurrencia.
La manifestación directa de la programación asíncrona en Node.js son las devoluciones de llamada (callbacks).

En Node.js, una función de devolución de llamada es un patrón de programación asíncrona que se utiliza para manejar operaciones de E/S, como lectura/escritura de archivos, interacción con bases de datos, solicitudes de red, etc. Estas operaciones suelen llevar mucho tiempo; si se usan de forma síncrona, bloquearían la ejecución de todo el programa hasta que la operación terminara.
Al usar funciones de devolución de llamada, Node.js puede continuar ejecutando otro código mientras se realiza la operación de E/S; una vez que la operación de E/S se completa, ejecuta la función de devolución de llamada.
Node.js es de un solo hilo, pero logra operaciones asíncronas mediante el mecanismo de eventos y devoluciones de llamada.
Escenarios de uso
- Operaciones de E/S como leer archivos, escribir archivos, etc.
- Manejar solicitudes de red.
- Consultas a bases de datos.
Por ejemplo, podemos leer un archivo y ejecutar otros comandos al mismo tiempo; cuando la lectura del archivo se completa, devolvemos el contenido del archivo como parámetro de la función de devolución de llamada. Así, al ejecutar el código no hay bloqueo ni espera por la operación de E/S del archivo. Esto mejora en gran medida el rendimiento de Node.js y le permite manejar una gran cantidad de solicitudes concurrentes.
Las funciones de devolución de llamada generalmente aparecen como el último parámetro de la función:
function foo1(name, age, callback) { }
function foo2(value, callback1, callback2) { }
Ejemplo
Ejemplo de código bloqueante
Crea un archivo input.txt con el siguiente contenido:
Ejemplo官网地址:www.example.com
Crea un archivo main.js con el siguiente código:
Ejemplo
var data = fs.readFileSync('input.txt');
console.log(data.toString());
console.log("¡Fin de la ejecución del programa!");
El resultado de ejecutar el código anterior es el siguiente:
$ node main.js Ejemplo官网地址:www.example.com 程序执行结束!
Ejemplo de código no bloqueante
Crea un archivo input.txt con el siguiente contenido:
Ejemplo官网地址:www.example.com
Crea un archivo main.js con el siguiente código:
Ejemplo
fs.readFile('input.txt', function (err, data) {
if (err) return console.error(err);
console.log(data.toString());
});
console.log("¡Fin de la ejecución del programa!");
Explicación de parámetros:
erres el objeto de error; si la lectura falla, tendrá un valor.dataes el contenido del archivo leído.
El resultado de ejecutar el código anterior es el siguiente:
$ node main.js 程序执行结束! Ejemplo官网地址:www.example.com
Con los dos ejemplos anteriores entendemos la diferencia entre llamadas bloqueantes y no bloqueantes:
El primer ejemplo ejecuta el programa solo después de que se completa la lectura del archivo.
En el segundo ejemplo no necesitamos esperar a que termine la lectura del archivo, de modo que podemos ejecutar el código siguiente mientras se lee el archivo, mejorando en gran medida el rendimiento del programa.
Por lo tanto, la ejecución bloqueante es secuencial, mientras que la no bloqueante no requiere orden. Por eso, si necesitas procesar los parámetros de la función de devolución de llamada, debes escribirlos dentro de la propia función de devolución de llamada.
Infierno de devoluciones de llamada (Callback Hell)
Cuando varias operaciones asíncronas deben ejecutarse en secuencia, las funciones de devolución de llamada provocan anidamiento del código, lo que dificulta su lectura y mantenimiento.
Ejemplo
if (err) {
console.error('Error reading file1:', err);
return;
}
fs.readFile('file2.txt', 'utf8', (err, data2) => {
if (err) {
console.error('Error reading file2:', err);
return;
}
fs.readFile('file3.txt', 'utf8', (err, data3) => {
if (err) {
console.error('Error reading file3:', err);
return;
}
console.log('Data from all files:', data1, data2, data3);
});
});
});
Para mejorar la legibilidad y el mantenimiento del código, se pueden utilizar los siguientes métodos:
- Promises:Promises es un nuevo patrón de programación asíncrona que te permite manejar operaciones asíncronas de forma encadenada, evitando el infierno de devoluciones de llamada.
- async/await:Basado en Promises,
async/awaitproporciona una forma de programación asíncrona más cercana al estilo de código síncrono, lo que hace que el código asíncrono sea más fácil de entender y mantener. - Programación dirigida por eventos:Node.js también admite la programación dirigida por eventos, que maneja operaciones asíncronas mediante la escucha y el disparo de eventos.
1. Usar async/await
async/await es una sintaxis introducida en ES2017 que te permite manejar operaciones asíncronas de forma más conveniente y evitar el infierno de devoluciones de llamada.
Ejemplo
async function readFiles() {
try {
const data1 = await fs.readFile('file1.txt', 'utf8');
const data2 = await fs.readFile('file2.txt', 'utf8');
const data3 = await fs.readFile('file3.txt', 'utf8');
console.log('Data from all files:', data1, data2, data3);
} catch (err) {
console.error('Error reading files:', err);
}
}
readFiles();
2. Usar Promises
Promises es otra forma de manejar operaciones asíncronas; puedes encadenar llamadas al método then, evitando devoluciones de llamada anidadas.
Ejemplo
fs.readFile('file1.txt', 'utf8')
.then(data1 => {
console.log('Data from file1:', data1);
return fs.readFile('file2.txt', 'utf8');
})
.then(data2 => {
console.log('Data from file2:', data2);
return fs.readFile('file3.txt', 'utf8');
})
.then(data3 => {
console.log('Data from file3:', data3);
})
.catch(err => {
console.error('Error reading files:', err);
});
Las funciones de devolución de llamada son una forma básica de manejar operaciones asíncronas en Node.js, pero a medida que aumenta la complejidad de las aplicaciones, el problema del infierno de devoluciones de llamada se vuelve cada vez más evidente. Al usar async/await o Promises, se puede mejorar significativamente la legibilidad y el mantenimiento del código.
Otras extensiones