La especificación ECMAScript es como un retrato del lenguaje JavaScript que se repinta cada año. Como es típico del JavaScript moderno, las especificaciones y la práctica del mundo real van en conjunto. La versión más nueva de la especificación, ECMAScript 2024incluye siete nuevas funciones de JavaScript y se espera que esté finalizado en junio. Este artículo presenta cuatro de las nuevas funciones que ya están disponibles en navegadores y entornos del lado del servidor, y listas para su uso hoy:
- Promesa.conResolvedores Es un poderoso mecanismo para gestionar operaciones asincrónicas cuando es necesario un control externo sobre la resolución y el rechazo.
- Objeto.groupBy y Map.groupBy le permite organizar colecciones basadas en propiedades clave.
- Atomics.waitAsync Facilita la comunicación segura y la sincronización entre subprocesos de trabajo.
- String.isWellFormed y String.toWellFormed agregue herramientas valiosas para manejar la entrada del usuario y los datos de la red.
Promesa.conResolvedores
Comencemos con el nuevo método estático en Promise
llamado withResolvers()
. Las promesas de JavaScript nos brindan varias formas de lidiar con operaciones asincrónicas. El withResolvers()
El método se utiliza para crear las tres partes de un Promise
: el Promise
mismo y el resolve()
y reject()
funciones.
El beneficio de withResolvers()
es que crea los tres como referencias expuestas externamente. En los casos en los que desee crear una promesa y también tener acceso a la resolución y rechazo de la promesa desde un código externo, este es el método a utilizar.
La especificación en sí es característicamente espartano en su descripción. La documentación de Mozilla proporciona más detalles. La conclusión clave es que withResolvers()
es equivalente a:
let resolve, reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
// use resolve and reject to control the promise
En el fragmento anterior, declaramos el resolve
y reject
referencias en el alcance adjunto, luego utilícelas dentro del cuerpo del Promise
devolución de llamada para referirse a la resolve
y reject
argumentos. De esta manera, podemos controlar la devolución de llamada de promesa desde fuera de la devolución de llamada misma.
El Promise.withResolvers()
la sintaxis es más compacta y no tenemos que declarar el resolve
y reject
por separado. Con este método, podríamos escribir exactamente la misma funcionalidad que la anterior, así:
let { promise, resolve, reject } = Promise.withResolvers();
La esencia de esta capacidad es que la utiliza cuando necesita acceso externo a resolve()
y reject()
. Este no es un escenario muy común pero sucede. Consideremos un ejemplo sencillo:
function race(operation1, operation2) {
const { racePromise, resolve, reject } = Promise.withResolvers();
operation1().then(resolve).catch(reject);
operation2().then(resolve).catch(reject);
return racePromise;
}
function fetchData1() {
return new Promise((resolve) => {
setTimeout(() => resolve("Data from source 1"), 1000);
});
}
function fetchData2() {
return new Promise((resolve) => {
setTimeout(() => resolve("Data from source 2"), 500);
});
}
race(fetchData1, fetchData2)
.then((data) => console.log("Winner:", data))
.catch((error) => console.error("Error:", error));
Aquí tenemos dos operaciones, fetchData1()
y fetchData2()
, ese retorno promete. Ejecutan tiempos de espera y fetchData2()
Siempre es más rápido con 500 milisegundos. Usamos withResolvers()
dentro de race()
función para exponer el resolve
y reject
funciones. Estas funciones luego son utilizadas por una nueva promesa, llamada racePromise
.
Luego usamos el resolve
y reject
funciones para responder a los dos fetchData
operaciones. Esto es interesante porque puedes ver que ni siquiera ofrecemos una devolución de llamada a racePromise
. En cambio, controlamos la promesa externamente. Usamos ese control para vincular el resultado de las otras dos promesas a racePromise
.
Este es un ejemplo artificial y algo poco realista porque las dos promesas de las carreras no comienzan al mismo tiempo. El punto es mostrar la esencia de cómo y cuándo usar. withResolvers()
.
Objeto.groupBy y Map.groupBy
el práctico método groupBy es una forma rápida de organizar colecciones basadas en un valor de cadena. Es un método estático en Object
y Map
, que funciona en una colección similar a una matriz. El groupBy()
El método toma dos argumentos: la colección y una devolución de llamada que opera en ella. Obtendrá una nueva instancia de objeto que tiene valores de etiqueta de cadena como claves y los elementos de matriz correspondientes como valor.
Entonces, cuando tiene una matriz y necesita dividir los elementos en depósitos etiquetados con cadenas de acuerdo con algunos criterios internos, este es el método a utilizar.
Esto es algo bastante común que surge en la codificación del día a día. Ver un ejemplo debería dejarlo claro. Digamos que tenemos una colección de razas de perros y su tamaño:
const dogBreeds = [
{ breed: 'Shih Tzu', size: 'Toy' },
{ breed: 'Leonberger', size: 'Giant' },
{ breed: 'King Charles Spaniel', size: 'Toy' },
{ breed: 'Great Pyrenees', size: 'Giant' },
{ breed: 'Corgi', size: 'Small' },
{ breed: 'German Shepherd', size: 'Large' },
];
Ahora digamos que queremos organizar esta colección por tamaño. Queremos terminar con una colección de objetos donde las claves sean el tamaño de la raza y los valores sean la raza del perro. Normalmente, escribiríamos un bucle para hacer esto, pero es un poco complicado; Parece que debería haber una mejor manera. Ahora con groupBy()
hay:
groupBy() is that better way:
Object.groupBy(dogBreeds, (x) => {
return x.size;
})
This gives us output like so:
{ "Toy": [
{ "breed": "Shih Tzu", "size": "Toy" },
{ "breed": "King Charles Spaniel", "size": "Toy" }
],
"Giant": [
{ "breed": "Leonberger", "size": "Giant" },
{ "breed": "Great Pyrenees", "size": "Giant" }
],
"Small": [
{ "breed": "Corgi", "size": "Small" }
],
"Large": [
{ "breed": "German Shepherd", "size": "Large" }
]
}
Esto le brinda una forma sencilla y funcional de agrupar colecciones de objetos en función de alguna propiedad de los propios objetos.
El groupBy()
El método toma lo que devuelve la devolución de llamada y recopila automáticamente todos los elementos que son iguales según String equality
. Si el valor de retorno no es una cadena, se convertirá en una cadena. Si no se puede forzar, se producirá un error.
Atomics.waitAsync
El nuevo Atomics.waitAsync()
El método está diseñado para compartir datos entre subprocesos de trabajo de forma segura. No bloquea el hilo principal como Atomics.wait()
hace. Debido a que se usa entre subprocesos, se basa en el SharedArrayBuffer
clase.
Esta clase está deshabilitada de forma predeterminada en los navegadores modernos. a menos que se cumplan los requisitos de seguridad. Sin embargo, en Node.js, la clase está habilitada de forma predeterminada.
A continuación se muestra un ejemplo sencillo de uso en Node. Tenga en cuenta que las importaciones están integradas en Node, por lo que no se requieren instalaciones de NPM (pero tenga en cuenta que el import
declaración es):
// asyncWait.js
const { Worker, isMainThread, parentPort } = require('worker_threads');
const sharedBuffer = new SharedArrayBuffer(4);
const int32Array = new Int32Array(sharedBuffer);
if (isMainThread) {
const worker = new Worker(__filename);
async function waitForMessage() {
const initialValue = 0;
const result = await Atomics.waitAsync(int32Array, 0, initialValue);
if (result.async) {
console.log("Message received from worker:", int32Array[0]);
} else {
console.error("Timeout waiting for worker message");
}
}
waitForMessage();
} else {
setTimeout(() => {
Atomics.store(int32Array, 0, 1);
}, 2000);
}
Para ejecutar este programa, simplemente ingrese: $ node asyncWait.js
El programa declara una SharedArrayBuffer
(envuelto alrededor de un int32Array
) y luego comprueba si estamos en el hilo principal. Si es el hilo principal, generamos un hilo de trabajo. (Si eres nuevo en los hilos de trabajo, aquí hay una buena introducción.)
El hilo principal espera una actualización del trabajador a través del Atomics.waitAsync()
llamar. Los tres argumentos para waitAsync(1, 2, 3)
son:
- Matriz compartida (
int32Array
): El espacio de memoria compartida. - Elemento a observar (
0
): El índice de la matriz a esperar. - Valor inicial (
initialValue = 0
): Notifique al hilo principal solo si este valor es diferente (es decir, si el trabajador establece el valor inicial de 0, no se producirá una notificación).
Cadena.isWellFormed y Cadena.toWellFormed
La entrada del usuario, los datos incorrectos y los fallos de la red son fuentes de cadenas con formato incorrecto. String.isWellFormed
es un control de cordura. Determina si una cadena es válida para UTF-16. UTF-16 es la codificación que utiliza JavaScript, por lo que String.isWellFormed()
garantiza que una cadena determinada sea algo que JavaScript pueda manejar:
const string1 = "Hello, InfoWorld!";
const string2 = "Hello, \uD800world!";
console.log(string1.isWellFormed()); // Output: true (well-formed)
console.log(string2.isWellFormed()); // Output: false (malformed)
Puede obtener más información sobre qué constituyen cadenas válidas en JavaScript en esta sección de la referencia de Mozilla. Los malos de la codificación UTF-16 se conocen como «sustitutos solitarios».
El socio activo de String.isWellFormed
, String.toWellFormed
transforma una cadena dada en algo válido. Cualquier sustituto solitario que se encuentre será reemplazado por U+FFFD, el carácter del signo de interrogación del diamante negro: �.
"Hello, \uD800world!".toWellFormed()
// outputs: 'Hello, �world!'
Conclusión
Tenemos una buena colección de funciones nuevas en ECMAScript 2024. Promise.withResolvers()
y Atomics.waitAsync()
son casos de uso más avanzados, mientras que groupBy
es una adición conveniente que a menudo resulta útil y los nuevos métodos de cadena son perfectos para determinadas situaciones. Todas estas funciones son compatibles con JavaScript en navegadores y entornos del lado del servidor, por lo que puede empezar a utilizarlas hoy mismo.
Copyright © 2024 IDG Communications, Inc.