Node.js 24 LTS: Todas las Nuevas Features de V8 13.6, npm 11 y Mejoras de Performance
Hola HaWkers, Node.js 24 llegó oficialmente en mayo de 2025 y está programado para entrar en LTS (Long-Term Support) en octubre. Esta versión trae una cantidad impresionante de nuevas features JavaScript, mejoras de performance y APIs modernizadas.
Si trabajas con JavaScript en el backend, esta guía te mostrará todo lo que necesitas saber sobre la nueva versión.
Overview de Node.js 24
Node.js 24 representa un salto significativo en capacidades.
Principales actualizaciones:
| Componente | Versión Anterior | Node.js 24 |
|---|---|---|
| V8 Engine | 12.x | 13.6 |
| npm | 10.x | 11 |
| Undici | 6.x | 7 |
| libuv | 1.48 | 1.50 |
Timeline de soporte:
- Lanzamiento: Mayo 2025
- LTS Activo: Octubre 2025 - Abril 2027
- Mantenimiento: Abril 2027 - Abril 2028
Nuevas Features JavaScript
V8 13.6 desbloquea varias features ECMAScript modernas.
RegExp.escape
Finalmente tenemos una forma nativa de escapar strings para uso en expresiones regulares.
// Antes - función manual (susceptible a bugs)
function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
// Ahora - método nativo
const userInput = "Hello [World]! Price: $100";
const escaped = RegExp.escape(userInput);
// Resultado: "Hello \\[World\\]! Price: \\$100"
// Uso práctico en búsqueda
function highlightText(text, searchTerm) {
const safeSearchTerm = RegExp.escape(searchTerm);
const regex = new RegExp(`(${safeSearchTerm})`, 'gi');
return text.replace(regex, '<mark>$1</mark>');
}
const result = highlightText(
"Precio: $50.00 (promoción)",
"$50.00"
);
// "<mark>$50.00</mark>" correctamente destacadoFloat16Array
Nuevo typed array para floats de 16 bits, ideal para ML y gráficos.
// Float16Array - mitad del tamaño de Float32Array
const float16Data = new Float16Array(4);
float16Data[0] = 1.5;
float16Data[1] = -2.25;
float16Data[2] = 3.14159; // Truncado para precisión de 16 bits
float16Data[3] = 0.0001;
console.log(float16Data.byteLength); // 8 bytes (vs 16 para Float32Array)
// Conversión entre tipos
const float32 = new Float32Array([1.5, 2.5, 3.5, 4.5]);
const float16 = new Float16Array(float32); // Conversión automática
// Útil para WebGL y Machine Learning
class NeuralNetworkLayer {
constructor(inputSize, outputSize) {
// Pesos en Float16 ahorran memoria
this.weights = new Float16Array(inputSize * outputSize);
this.biases = new Float16Array(outputSize);
}
forward(input) {
// Computación con precisión reducida pero suficiente
const output = new Float16Array(this.biases.length);
// ... implementación
return output;
}
}Error.isError
Verificación nativa si un valor es realmente un objeto Error.
// Antes - verificación manual (falla con objetos de otros realms)
function isErrorOld(value) {
return value instanceof Error;
}
// Ahora - método nativo (funciona entre realms)
console.log(Error.isError(new Error('test'))); // true
console.log(Error.isError(new TypeError('test'))); // true
console.log(Error.isError(new RangeError('test'))); // true
console.log(Error.isError({ message: 'fake error' })); // false
console.log(Error.isError(null)); // false
// Uso práctico en error handling
function handleResponse(result) {
if (Error.isError(result)) {
logger.error('Operation failed:', result.message);
return { success: false, error: result.message };
}
return { success: true, data: result };
}
// Funciona con errores de iframes/workers
const iframe = document.createElement('iframe');
document.body.appendChild(iframe);
const IframeError = iframe.contentWindow.Error;
const iframeError = new IframeError('from iframe');
console.log(iframeError instanceof Error); // false (diferentes realms)
console.log(Error.isError(iframeError)); // true (¡funciona!)
Atomics.pause
Nueva primitiva para sincronización de bajo nivel en Workers.
// Atomics.pause - útil para spin-waiting eficiente
const sharedBuffer = new SharedArrayBuffer(4);
const sharedArray = new Int32Array(sharedBuffer);
// Worker esperando por valor
function waitForValue(expected) {
while (Atomics.load(sharedArray, 0) !== expected) {
// Pause permite que la CPU optimice el spin-wait
Atomics.pause();
}
return sharedArray[0];
}
// Main thread señalizando
function signalWorker(value) {
Atomics.store(sharedArray, 0, value);
Atomics.notify(sharedArray, 0);
}
// Implementación de spinlock
class SpinLock {
constructor(buffer, index) {
this.array = new Int32Array(buffer);
this.index = index;
}
acquire() {
while (Atomics.compareExchange(this.array, this.index, 0, 1) !== 0) {
Atomics.pause(); // CPU hint para aguardar
}
}
release() {
Atomics.store(this.array, this.index, 0);
}
}WebAssembly Memory64
Soporte a memoria de 64 bits para WebAssembly.
// WebAssembly con memoria de 64 bits
const memory64 = new WebAssembly.Memory({
initial: 1,
maximum: 65536, // Hasta 4TB de memoria
index: 'i64', // Indexación 64-bit
});
// Permite direccionar más de 4GB de memoria
// Útil para aplicaciones de big data y ML
// Ejemplo de uso con módulo WASM
const wasmCode = await fetch('/module.wasm');
const wasmBuffer = await wasmCode.arrayBuffer();
const wasmModule = await WebAssembly.instantiate(wasmBuffer, {
env: {
memory: memory64,
},
});
// Procesar datasets mayores que 4GB directamente en WASM
const exports = wasmModule.instance.exports;
const result = exports.processLargeDataset(dataPointer, dataSize);
AsyncLocalStorage Mejorado
AsyncLocalStorage ahora usa AsyncContextFrame por defecto.
import { AsyncLocalStorage } from 'node:async_hooks';
const requestContext = new AsyncLocalStorage();
// Middleware Express/Fastify
function contextMiddleware(req, res, next) {
const context = {
requestId: crypto.randomUUID(),
userId: req.user?.id,
startTime: Date.now(),
};
requestContext.run(context, () => {
next();
});
}
// Logger que automáticamente incluye contexto
class ContextLogger {
log(message, data = {}) {
const ctx = requestContext.getStore();
console.log(JSON.stringify({
timestamp: new Date().toISOString(),
requestId: ctx?.requestId,
userId: ctx?.userId,
message,
...data,
}));
}
// Nuevo: Performance mejorada con AsyncContextFrame
async logAsync(message) {
// Contexto preservado incluso en operaciones async complejas
await someAsyncOperation();
this.log(message);
}
}
// Uso en cualquier lugar del código
const logger = new ContextLogger();
async function processOrder(orderId) {
logger.log('Processing order', { orderId });
// Incluso en callbacks anidados, contexto es preservado
await db.transaction(async (trx) => {
logger.log('Starting transaction');
await trx.insert('orders', { id: orderId });
logger.log('Order inserted');
});
logger.log('Order processed');
}Mejoras de performance:
| Operación | Node.js 22 | Node.js 24 |
|---|---|---|
| getStore() | ~150ns | ~50ns |
| run() overhead | ~500ns | ~200ns |
| Nested contexts | Linear | Constante |
URLPattern Global
La API URLPattern ahora está disponible globalmente.
// Antes - necesario import
// import { URLPattern } from 'urlpattern-polyfill';
// Ahora - disponible globalmente
const pattern = new URLPattern({
pathname: '/users/:userId/posts/:postId',
});
// Matching de URLs
const url1 = 'https://api.example.com/users/123/posts/456';
const match = pattern.exec(url1);
console.log(match.pathname.groups);
// { userId: '123', postId: '456' }
// Patterns más complejos
const apiPattern = new URLPattern({
protocol: 'https',
hostname: '*.example.com',
pathname: '/api/v:version/:resource{/:id}?',
});
const testUrls = [
'https://api.example.com/api/v2/users',
'https://api.example.com/api/v2/users/123',
'https://cdn.example.com/api/v1/files/abc',
];
testUrls.forEach(url => {
const result = apiPattern.exec(url);
if (result) {
console.log('Match:', {
subdomain: result.hostname.groups[0],
version: result.pathname.groups.version,
resource: result.pathname.groups.resource,
id: result.pathname.groups.id,
});
}
});
// Router simple usando URLPattern
class PatternRouter {
constructor() {
this.routes = [];
}
add(method, patternString, handler) {
this.routes.push({
method,
pattern: new URLPattern({ pathname: patternString }),
handler,
});
}
match(method, url) {
for (const route of this.routes) {
if (route.method !== method) continue;
const match = route.pattern.exec(url);
if (match) {
return { handler: route.handler, params: match.pathname.groups };
}
}
return null;
}
}
const router = new PatternRouter();
router.add('GET', '/users/:id', getUserHandler);
router.add('POST', '/users', createUserHandler);
router.add('GET', '/posts/:postId/comments/:commentId?', getCommentsHandler);
npm 11
npm 11 trae mejoras significativas.
Performance de Instalación
# Instalación más rápida con cache optimizado
npm install
# Benchmark comparativo (proyecto medio ~500 deps)
# npm 10: 45 segundos
# npm 11: 28 segundos (-38%)
# Nuevo algoritmo de resolución de dependencias
# Menos duplicación, árbol más flatNuevos Comandos y Flags
# Query mejorada de dependencias
npm query ":root > :has([name^=@types/])"
# Lista todas las dependencias de tipos directas
# Audit con más detalles
npm audit --format=json --detail
# Pack con preview
npm pack --dry-run --json
# Muestra exactamente lo que sería incluido
# Instalación selectiva
npm install --omit=dev --omit=optional --omit=peer
# Verificación de integridad mejorada
npm doctor --fix
# Corrige problemas automáticamente cuando posibleWorkspaces Mejorados
# Ejecución paralela en workspaces
npm run build --workspaces --parallel
# Filtrado por workspace
npm run test --workspace=packages/core --workspace=packages/utils
# Publicación coordinada
npm publish --workspaces --tag latest
# Dependencias entre workspaces
npm install @myorg/utils --workspace=packages/core
Test Runner Nativo Mejorado
El test runner de Node.js recibió mejoras significativas.
import { describe, it, before, after, mock } from 'node:test';
import assert from 'node:assert';
describe('UserService', () => {
let db;
let service;
before(async () => {
db = await createTestDatabase();
service = new UserService(db);
});
after(async () => {
await db.close();
});
// Nuevo: Subtests no necesitan más await explícito
it('should create user', async (t) => {
const user = await service.create({
name: 'Test User',
email: 'test@example.com',
});
// Subtests gestionados automáticamente
t.test('should have valid id', () => {
assert.ok(user.id);
assert.match(user.id, /^[a-f0-9-]{36}$/);
});
t.test('should have timestamps', () => {
assert.ok(user.createdAt instanceof Date);
assert.ok(user.updatedAt instanceof Date);
});
});
// Nuevo: Mocking mejorado
it('should send welcome email', async (t) => {
const sendEmail = t.mock.fn(async () => ({ sent: true }));
// Mock de módulo
t.mock.module('../email-service.js', {
namedExports: { sendEmail },
});
await service.create({ name: 'Test', email: 'test@test.com' });
assert.strictEqual(sendEmail.mock.calls.length, 1);
assert.strictEqual(
sendEmail.mock.calls[0].arguments[0],
'test@test.com'
);
});
// Nuevo: Snapshot testing
it('should format user correctly', async (t) => {
const user = await service.getById('user-123');
t.assert.snapshot(user);
});
});Ejecutando tests:
# Ejecutar todos los tests
node --test
# Con coverage
node --test --experimental-test-coverage
# Watch mode
node --test --watch
# Filtrar por nombre
node --test --test-name-pattern="UserService"
# Reporter personalizado
node --test --test-reporter=spec
Undici 7
El cliente HTTP estándar fue actualizado para Undici 7.
import { request, fetch, Agent, Pool } from 'undici';
// Fetch con más control
const response = await fetch('https://api.example.com/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ query: 'test' }),
// Nuevo: Control de timeout granular
signal: AbortSignal.timeout(5000),
});
// Pool de conexiones optimizado
const pool = new Pool('https://api.example.com', {
connections: 10,
pipelining: 6,
keepAliveTimeout: 30000,
keepAliveMaxTimeout: 60000,
});
// Requests usando pool
const { statusCode, body } = await pool.request({
path: '/users',
method: 'GET',
});
const data = await body.json();
// Agent customizado para retry
const retryAgent = new Agent({
connect: {
timeout: 10000,
},
// Nuevo: Retry automático
retry: {
maxRetries: 3,
minTimeout: 100,
maxTimeout: 1000,
},
});
// Streaming mejorado
const streamResponse = await fetch('https://api.example.com/stream', {
dispatcher: retryAgent,
});
for await (const chunk of streamResponse.body) {
process.stdout.write(chunk);
}
Deprecaciones y Remociones
Algunas APIs fueron deprecadas o removidas.
url.parse() Deprecado
// Deprecado
import { parse } from 'node:url';
const parsed = parse('https://example.com/path?query=1');
// Recomendado: WHATWG URL API
const url = new URL('https://example.com/path?query=1');
console.log(url.hostname); // 'example.com'
console.log(url.pathname); // '/path'
console.log(url.searchParams.get('query')); // '1'
// Migración de código legado
// Antes
const oldUrl = parse('https://user:pass@example.com:8080/path?q=1#hash');
console.log(oldUrl.auth); // 'user:pass'
// Después
const newUrl = new URL('https://user:pass@example.com:8080/path?q=1#hash');
console.log(newUrl.username); // 'user'
console.log(newUrl.password); // 'pass'tls.createSecurePair Removido
// Removido: tls.createSecurePair()
// Usa tls.TLSSocket directamente
import tls from 'node:tls';
import net from 'node:net';
const socket = net.connect(443, 'example.com');
const tlsSocket = new tls.TLSSocket(socket, {
// opciones
});
Migrando desde Node.js 22
Guía práctica para actualización.
Checklist de Migración
# 1. Verificar versión actual de las dependencias
npm outdated
# 2. Actualizar dependencias que pueden tener problemas
npm update
# 3. Verificar uso de APIs deprecadas
npx depcheck
# 4. Testar con Node.js 24
nvm install 24
nvm use 24
npm test
# 5. Verificar compatibilidad de native modules
npm rebuild
# 6. Actualizar engines en package.json{
"engines": {
"node": ">=24.0.0"
}
}Breaking Changes Importantes
Cambios que pueden afectar tu código:
url.parse()ahora emite warning de deprecacióntls.createSecurePairfue removido- Algunas flags experimentales fueron removidas
--experimental-specifier-resolutionremovido (usa import maps)
Conclusión
Node.js 24 representa una evolución significativa de la plataforma. Con V8 13.6, ganamos features JavaScript modernas como RegExp.escape y Float16Array. npm 11 trae instalaciones más rápidas y workspaces mejorados. El test runner nativo está cada vez más completo.
Para quien está en producción con Node.js 20 o 22, la migración para 24 LTS (cuando disponible en octubre) será tranquila para la mayoría de los proyectos. Las deprecaciones son pocas y bien documentadas.
Si quieres explorar más sobre el ecosistema JavaScript moderno, confiere nuestro artículo sobre ECMAScript 2025 y Nuevas Features JavaScript.
¡Vamos a por ello! 🦅
📚 ¿Quieres Dominar Node.js del Zero al Avanzado?
Este artículo mostró las novedades de Node.js 24, pero para aprovechar todo el potencial de la plataforma, necesitas fundamentos sólidos.
Material de Estudio Completo
Si quieres construir una base sólida en JavaScript para dominar Node.js:
Opciones de inversión:
- 1x de R$9,90 en tarjeta
- o R$9,90 al contado
💡 JavaScript sólido = Node.js dominado

