JavaScript no deja de evolucionar, y con ES11 (también conocido como ECMAScript 2020), llegaron nuevas características que hacen nuestro código más limpio, eficiente y fácil de entender.
Si eres desarrollador, estas mejoras te ayudarán a escribir código más moderno y mantenible. Veamos que características nos trae esta versión.
1. Operador de Coalescencia Nula (??
)
El operador de coalescencia nula (??
) es una forma más segura de manejar valores null
o undefined
. A diferencia del operador ||
, que considera falsy values como 0
, ''
, o false
, ??
solo devuelve el valor de la derecha si el de la izquierda es null
o undefined
.
const valorNulo = null;
const valorPorDefecto = valorNulo ?? "Valor por defecto"; // "Valor por defecto"
const valorCero = 0;
const resultado = valorCero ?? 42; // 0 (porque 0 no es null ni undefined)
2. Encadenamiento Opcional (?.
)
El encadenamiento opcional (?.
) es una forma segura de acceder a propiedades anidadas de un objeto sin tener que verificar manualmente si cada nivel existe. Si alguna propiedad en la cadena es null
o undefined
, la expresión se detiene y devuelve undefined
.
const usuario = {
nombre: "Carlos",
direccion: {
ciudad: "Madrid",
},
};
console.log(usuario.direccion?.ciudad); // "Madrid"
console.log(usuario.telefono?.numero); // undefined (no hay error)
3. Promise.allSettled()
Promise.allSettled()
es un método que toma un iterable de promesas (por ejemplo, un array de promesas) y devuelve una nueva promesa que se resuelve cuando todas las promesas del iterable se han resuelto o rechazado.
A diferencia de Promise.all()
, que se detiene si una promesa falla, Promise.allSettled()
siempre espera a que todas las promesas terminen, sin importar si fueron resueltas o rechazadas.
El resultado es un array de objetos que describen el resultado de cada promesa. Cada objeto tiene dos propiedades:
value
oreason
: Si la promesa fue resuelta, contiene el valor resuelto (value
). Si fue rechazada, contiene la razón del rechazo (reason
).status
: Indica si la promesa fue resuelta ("fulfilled"
) o rechazada ("rejected"
).
Ahora te voy a enseñar varios ejemplos para que le saques el mayor provecho.
Ejemplo básico
Voy a darte en este ejemplo un array de promesas, algunas de las cuales pueden fallar. Con Promise.allSettled()
, puedes manejar todos los resultados sin preocuparte por errores inesperados.
const promesas = [
Promise.resolve("Éxito 1"),
Promise.reject("Error 1"),
Promise.resolve("Éxito 2"),
Promise.reject("Error 2"),
];
Promise.allSettled(promesas).then(resultados => {
resultados.forEach((resultado, index) => {
if (resultado.status === "fulfilled") {
console.log(`Promesa ${index}: Éxito con valor ${resultado.value}`);
} else {
console.log(`Promesa ${index}: Error con razón ${resultado.reason}`);
}
});
});
/*
Salida:
Promesa 0: Éxito con valor Éxito 1
Promesa 1: Error con razón Error 1
Promesa 2: Éxito con valor Éxito 2
Promesa 3: Error con razón Error 2
*/
Ejemplo Práctico: Múltiples Solicitudes HTTP
Digamos que estás haciendo varias solicitudes HTTP a diferentes APIs y quieres manejar todos los resultados, incluso si algunas fallan. Promise.allSettled()
es perfecto para esto.
const urls = [
"https://jsonplaceholder.typicode.com/posts/1",
"https://jsonplaceholder.typicode.com/posts/99999", // Esta URL no existe
"https://jsonplaceholder.typicode.com/posts/2",
];
const solicitudes = urls.map(url =>
fetch(url)
.then(response => response.json())
.catch(error => ({ error: true, mensaje: error.message }))
);
Promise.allSettled(solicitudes).then(resultados => {
resultados.forEach((resultado, index) => {
if (resultado.status === "fulfilled") {
console.log(`Solicitud ${index}:`, resultado.value);
} else {
console.log(`Solicitud ${index}:`, resultado.reason);
}
});
});
/*
Salida:
Solicitud 0: { userId: 1, id: 1, title: "...", body: "..." }
Solicitud 1: { error: true, mensaje: "Failed to fetch" }
Solicitud 2: { userId: 1, id: 2, title: "...", body: "..." }
*/
Ejemplo Práctico: Validación de Datos
Imagina que tienes un formulario con varios campos y quieres validar cada campo de manera independiente. Puedes usar Promise.allSettled()
para validar todos los campos y mostrar los errores de los que fallaron.
const validarCampo = (valor, regla) => {
return new Promise((resolve, reject) => {
if (regla(valor)) {
resolve(`${valor} es válido`);
} else {
reject(`${valor} no es válido`);
}
});
};
const campos = [
{ valor: "usuario", regla: valor => valor.length >= 5 },
{ valor: "123", regla: valor => valor.length >= 6 },
{ valor: "correo@ejemplo.com", regla: valor => valor.includes("@") },
];
const promesas = campos.map(campo => validarCampo(campo.valor, campo.regla));
Promise.allSettled(promesas).then(resultados => {
resultados.forEach((resultado, index) => {
if (resultado.status === "fulfilled") {
console.log(`Campo ${index}:`, resultado.value);
} else {
console.log(`Campo ${index}:`, resultado.reason);
}
});
});
/*
Salida:
Campo 0: usuario es válido
Campo 1: 123 no es válido
Campo 2: correo@ejemplo.com es válido
*/
Ejemplo Práctico: Procesamiento en Paralelo
Supongamos que tienes una lista de tareas que deben procesarse en paralelo, pero algunas pueden fallar. Con Promise.allSettled()
, puedes manejar los resultados de todas las tareas, incluso si algunas no se completan correctamente.
const tareas = [
new Promise(resolve => setTimeout(() => resolve("Tarea 1 completada"), 1000)),
new Promise((resolve, reject) => setTimeout(() => reject("Tarea 2 falló"), 500)),
new Promise(resolve => setTimeout(() => resolve("Tarea 3 completada"), 1500)),
];
Promise.allSettled(tareas).then(resultados => {
resultados.forEach((resultado, index) => {
if (resultado.status === "fulfilled") {
console.log(`Tarea ${index}:`, resultado.value);
} else {
console.log(`Tarea ${index}:`, resultado.reason);
}
});
});
/*
Salida:
Tarea 0: Tarea 1 completada
Tarea 1: Tarea 2 falló
Tarea 2: Tarea 3 completada
*/
4. BigInt
BigInt
es un nuevo tipo de dato que permite trabajar con números enteros de cualquier tamaño. Esto es útil cuando necesitas manejar números más grandes de lo que Number
puede soportar.
const numeroGrande = 9007199254740991n; // La 'n' al final indica que es un BigInt
const otroNumeroGrande = BigInt("123456789012345678901234567890");
console.log(numeroGrande + otroNumeroGrande); // 123456789012345678910241767881n
5. String.prototype.matchAll()
El método matchAll()
devuelve un iterador con todas las coincidencias de una expresión regular en un string, incluyendo los grupos de captura. Esto es útil cuando necesitas extraer información específica de un texto.
const texto = "Llamame al 123-456-7890 o al 987-654-3210";
const regex = /(\d{3})-(\d{3})-(\d{4})/g;
for (const coincidencia of texto.matchAll(regex)) {
console.log(coincidencia[0]); // "123-456-7890", "987-654-3210"
}
6. globalThis
globalThis
es una forma universal de acceder al objeto global (window
en navegadores, global
en Node.js, etc.). Esto simplifica el código cuando trabajas en diferentes entornos.
console.log(globalThis === window); // true en navegadores
console.log(globalThis === global); // true en Node.js
Conclusión
ES11 trae mejoras que, aunque no revolucionan el lenguaje, sí nos hacen la vida más fácil. Desde operadores como ??
y ?.
hasta nuevas funcionalidades como Promise.allSettled()
y BigInt
, estas características están diseñadas para que escribamos código más limpio y eficiente.
5 Retos para Practicar
- Usa el operador
??
para asignar un valor por defecto a una variable que pueda sernull
oundefined
. - Accede a una propiedad anidada de un objeto usando encadenamiento opcional (
?.
). - Crea un array de promesas y usa
Promise.allSettled()
para manejar los resultados. - Suma dos números grandes usando
BigInt
y muestra el resultado. - Extrae todos los números de teléfono de un string usando
matchAll()
.
¿Te animas a resolver estos retos? ¡Compártelos en la caja de comentarios y dinos cómo te fue! Si tienes dudas o quieres aportar algo, no dudes en comentar 🚀.
Sigueme en mis redes sociales para más contenido