En Javascript, tradicionalmente utilizabamos var
para declarar elementos y variables, pero con la llegada de ECMAScript 2015 se trasladó a utilizar const
y let
. Recientemente, llega una forma nueva de declarar elementos mediante la palabra clave using
.
La diferencia respecto a los anteriores, es que using
permite gestionar de forma controlada los recursos, pudiendo lanzar tareas de limpieza o gestión cuando se destruye el elemento al llegar a su final de ámbito.
La palabra clave using
Veamos un ejemplo sencillo para empezar. Observa las dos formas de declarar un elemento en Javascript. Ambas son válidas y funcionan correctamente. Estoy utilizando new URL()
para declarar una URL, pero no es importante, es simplemente un ejemplo:
const element = new URL("https://manz.dev/");
using element = new URL("https://manz.dev/");
Este fragmento de código funcionará perfectamente y no observarás diferencias, ya que los elementos son globales y siempre están disponibles en un ámbito global. Ahora, vamos a meter esta declaración con using
en un ámbito limitado (dentro de las llaves), para forzar a que esa variable se destruye al terminar su ámbito.
En este ejemplo, yo lo he hecho simplemente con llaves, pero podría hacerse en un ámbito de un bucle for
, de un condicional if
, etc:
{
using element = new URL("https://manz.dev/");
// Aquí termina la existencia de "element"
}
// Aquí "element" ya no existe
Si intentamos ejecutar este código, ahora si, obtendremos el siguiente error:
Uncaught TypeError: Symbol(Symbol.dispose) is not a function
Este error ocurre porque al utilizar using
el sistema espera que exista una función con nombre Symbol.dispose
en ese elemento, que es la que se ejecutará al destruir el elemento. En este ejemplo, no existe tal función, y por eso muestra ese error.
Si no conoces los Símbolos en Javascript, te recomiendo que los leas antes de continuar.
La función de limpieza
El símbolo Symbol.dispose
Vamos a ampliar un poco nuestro ejemplo anterior, añadiéndole un Symbol.dispose
como debe ser en cualquier elemento donde utilicemos using
. Observa que al necesitar añadir varios elementos en el objeto, no puede ser simple, por lo que nuestro elemento va a pasar a tener dos propiedades.
Piensa con el siguiente modelo mental:
const element = {
value: /* ... */,
dispose: /* ... */
};
En nuestro nuevo ejemplo, haremos algunos cambios en ese modelo mental:
- 1️⃣ En lugar de utilizar
const
utilizaremosusing
- 2️⃣ Nuestra información, pasará a estar dentro de la propiedad
value
- 3️⃣ En lugar de utilizar
dispose
, utilizaremos[Symbol.dispose]
.
Nuestro ejemplo anterior quedaría como se ve en el siguiente fragmento de código:
{
using element = {
value: new URL("https://google.com/"),
[Symbol.dispose]: () => console.log("Liberando recursos...")
}
console.log(element.value);
}
Observa que [Symbol.dispose]
es una propiedad donde se guarda una función. Dicha función se va a ejecutar automáticamente cuando llegue al final de su vida y se destruya el elemento, actuando como una especie de vigilante que estará diseñado para ejecutar funciones orientadas a liberar recursos o limpiar información que no va a ser necesaria más adelante.
Si te confunde la sintaxis de
[Symbol.dispose]
o te parece rara, simplemente imagina que esa parte esdispose
, pero con una sintaxis diferente porque es una situación especial.
El símbolo Symbol.asyncDispose
De la misma forma que utilizamos [Symbol.dispose]
en el ejemplo anterior, puede ser necesario querer utilizar operaciones asíncronas en la función de limpieza, como por ejemplo, acceder mediante un fetch()
a una API con información necesaria para la limpieza.
En ese caso, haremos los siguientes cambios:
- 1️⃣ En lugar de
using
utilizaremosawait using
- 2️⃣ En lugar de
[Symbol.dispose]
utilizaremos[Symbol.asyncDispose]
- 3️⃣ Le añadiremos el
async
a la función de limpieza
El código quedaría como sigue:
{
await using element = {
value: new URL("https://google.com/"),
[Symbol.asyncDispose]: async () => console.log("Liberando recursos...")
}
console.log(element.value);
}
Ahora podremos hacer uso de operaciones asíncronas en su interior.
Uso de clases OOP
Obviamente, en estos ejemplos hemos utilizado un simple
class DisposableURL {
url;
constructor(value) {
this.url = new URL(value);
}
[Symbol.dispose]() {
console.log("Liberando recursos...");
}
}
Una vez declarada nuestra clase, ahora podemos utilizar la siguiente sintaxis, mucho más limpia y clara:
{
using element = new DisposableURL("https://manz.dev/");
console.log(element.url);
}