<andres-carmona />

TIL #11: Symbol.asyncIterator
First published on
on html, javascript

Symbol.asyncIterator

Estoy re-leyendo algunas partes de la referencia de Javascript en la MDN y me encuentro con varias características de Javascript que para mi eran desconocidas y que son muy interesantes.

En este caso, un objeto con una propiedad tipo Symbol.asyncIterator que a la vez es un generador que puede producir (yield) un resultado, que después se usa un ciclo for await. ¡Simplemente genial!.

const delayedResponses = {
  delays: [500, 1300, 3500],

  wait(delay) {
    return new Promise((resolve) => {
      setTimeout(resolve, delay);
    });
  },

  async *[Symbol.asyncIterator]() {
    for (const delay of this.delays) {
      await this.wait(delay);
      yield `Delayed response for ${delay} milliseconds`;
    }
  },
};

(async () => {
  for await (const response of delayedResponses) {
    console.log(response);
  }
})();

// Expected output: "Delayed response for 500 milliseconds"
// Expected output: "Delayed response for 1300 milliseconds"
// Expected output: "Delayed response for 3500 milliseconds"

TIL #10: Javascript in 2026
First published on
on html, javascript

Estaba leyendo este excelente artículo en Frontend Masters donde se describen algunas de las nuevas características presentes en Javascript y otras más que se esperan para este año o finales del mismo, entre ellas las que más destaco son las siguientes:

Iterator.from:

const result = Iterator.from(array)
  .map(x => x * 2)
  .filter(x => x > 10)
  .take(3)
  .toArray(); // No new arrays created, computation stops after 3

Métodos en Set:

const youKnow  = new Set(["JS", "Python", "CSS", "SQL"]);
const jobNeeds  = new Set(["JS", "TypeScript", "Python"]);

// Skills the job wants that you already have
youKnow.intersection(jobNeeds); // → Set {"JS", "Python"}

// Everything combined — your full stack + job needs
youKnow.union(jobNeeds); // → Set {"JS", "Python", "CSS", "SQL", "TypeScript"}

// What the job needs that you DON'T know yet (skill gaps)
jobNeeds.difference(youKnow); // → Set {"TypeScript"}

// Skills you have that the job doesn't care about
youKnow.difference(jobNeeds); // → Set {"CSS", "SQL"}

// Skills that appear in only one set, not both
youKnow.symmetricDifference(jobNeeds); // → Set {"CSS", "SQL", "TypeScript"}

// Are all job requirements a subset of what you know?
jobNeeds.isSubsetOf(youKnow); // → false

// Do you have every skill and more?
youKnow.isSupersetOf(jobNeeds); // → false

// Do you and the job have zero overlap?
youKnow.isDisjointFrom(jobNeeds); // → false

Promise.try:

Promise.try(() => loadUser(id))
  .then(user  => render(user))
  .catch(err  => showError(err));  

Import attributes: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import/with

import data from "./file.json" with { type: 'json' }

import exampleStyles from "https://example.com/example_styles.css" with { type: "css" };

document.adoptedStyleSheets.push(exampleStyles);

using y await using: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/using

class FileHandle {
  constructor(path) {
    this.path = path;
    console.log(`Opened ${path}`);
  }

  async write(data) {
    // ... write data
  }

  async [Symbol.asyncDispose]() {
    await someFlushOperation();
    console.log(`Flushed and closed ${this.path}`);
  }
}

async function saveData() {
  await using file = new FileHandle("output.txt");
  await file.write("hello world");
  // file is automatically flushed + closed here, even if an error is thrown
}

Array.fromAsync: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fromAsync

function createAsyncIter() {
  let i = 0;
  return {
    [Symbol.asyncIterator]() {
      return {
        async next() {
          if (i > 2) return { done: true };
          i++;
          return { value: Promise.resolve(i), done: false };
        },
      };
    },
  };
}

Array.fromAsync(createAsyncIter()).then((array) => console.log(array));

Intl.Collator:

const words = ['äpfel', 'Zebra', 'Bär', 'Apfel', 'über'];
const collator = new Intl.Collator('de');

const sorted = words.sort(collator.compare);
console.log(sorted); // ['Apfel', 'äpfel', 'Bär', 'über', 'Zebra']

TIL #9: ownerDocument.defaultView
First published on
on html, javascript, typescript

Aunque me considero un programador con experiencia y llevo más de 15 años desarrollando aplicaciones web, me sorprendió no conocer la propiedad ownerDocument.defaultView.

Básicamente, devuelve el objeto window (o null) asociado a un nodo DOM especifico.

Se puede usar para adjuntar listeners de forma segura al window correcto desde un componente que se renderiza fuera de la ventana principal (por ejemplo, usando portales).

function ThemeProvider({ children }) {
  const [theme, setTheme] = useState("light");
  const ref = useRef(null);

  useEffect(() => {
    const win = ref.current?.ownerDocument.defaultView || window;

    const toggle = (e) => {
      if (e.metaKey && e.key === "d") {
        e.preventDefault();
        setTheme((t) => (t === "dark" ? "light" : "dark"));
      }
    };

    win.addEventListener("keydown", toggle);

    return () => win.removeEventListener("keydown", toggle);
  }, []);

  return (
    <div ref={ref} className={theme}>
      {children}
    </div>
  );
}

Del mismo articulo tambien aprendí sobre useId y cache.

Referencia: https://shud.in/thoughts/build-bulletproof-react-components, gran artículo.


Atajos de Blender
First published on
on css, html

Estaba revisando una página que tengo en Notion sobre Three.js, con marcadores, links a artículos, utilidades, herramientas y repositorios, y encontré un recurso muy bueno sobre atajos de Blender.

Algunos no los conocía (también porque apenas estoy empezando con Blender), como los atajos de Snap o Clear transform, y quise compartirlo por aquí:

http://hollisbrown.github.io/blendershortcuts

TIL #7: Filtros SVG
First published on
on css, html, svg, effects, filters

Los filtros SVG te permiten crear efectos visuales complejos directamente dentro de tus graficos SVG usando distintos primitives. Puedes aplicar blur, manipulacion de color y distorsiones personalizadas.

Filtros SVG

<p class="ripple-text">Filtros SVG</p>

<svg style="position: absolute; width: 0px; height: 0px; pointer-events: none;">
  <filter id="water-ripple">
    <feTurbulence
      type="fractalNoise"
      baseFrequency="0.05"
      numOctaves="2"
      result="ripple"
    >
      <animate
        attributeName="baseFrequency"
        dur="10s"
        values="0.02;0.05;0.02"
        repeatCount="indefinite"
      ></animate>
    </feTurbulence>
    <feDisplacementMap
      in="SourceGraphic"
      in2="ripple"
      scale="5"
    ></feDisplacementMap>
  </filter>
</svg>
.ripple-text {
  font-size: clamp(2rem, 4vw, 3rem);
  font-weight: bold;
  text-align: center;
  filter: url(#water-ripple);
}

Usar 100vw ahora considera la barra de scroll (en Chrome 145+, bajo ciertas condiciones)
First published on
on css, html

Desde Chrome 145, 100vw resta automáticamente el ancho de la barra de scroll vertical si forzaste al elemento html a mostrar siempre scroll vertical (con overflow[-y]: scroll) o si reservaste espacio con scrollbar-gutter: stable.

Via https://www.bram.us/2026/01/15/100vw-horizontal-overflow-no-more/


TIL #6: Favicons SVG
First published on
on css, html, svg, icons
<svg xmlns="http://w3.org/2000/svg" viewBox="0 0 100 100">
  <text y=".9em" font-size="90">👾</text>
</svg>

Referencia:

Mas sobre SVG favicons y como mantener favicons sin perder la cordura:


TIL #4: CSS content-visibility para mejorar performance
First published on
on css, html, performance

content-visiblity Without Jittery Scrollbars

/* Diferir el render del segundo articulo en adelante */
body > main > *+* {
  content-visibility: auto;
}
<script type="module">
  let observer = new IntersectionObserver(
    (entries, o) => {
      entries.forEach((entry) => {
        let el = entry.target;
        // No esta actualmente dentro del area de interseccion.
        if (entry.intersectionRatio == 0) {
          return;
        }
        // Fuerza el render para elementos dentro del area de scroll
        // que aun no hayan sido marcados.
        if (!el.markedVisible) {
          el.attributeStyleMap.set(
            "content-visibility",
            "visible"
          );
          el.markedVisible = true;
        }
      });
    },
    // Define un margen de render de 50px arriba
    // y 100px debajo del area principal.
    { rootMargin: "50px 0px 100px 0px" }
  );

  let els =
    document.querySelectorAll("body > main > *+*");
  els.forEach((el) => { observer.observe(el); });
</script>

TIL #3: Configuracion minima para view transitions
First published on
on html, css, view transitions

La configuracion minima para habilitar view transitions nativas en tu sitio con HTML y CSS:

<meta name="view-transition" content="same-origin" />
@view-transition {
  navigation: auto;
}

Ref URL: https://www.amitmerchant.com/bare-minimum-view-transitions/


TIL #2: Animar elemento details
First published on
on css, html, css-animations

https://developer.chrome.com/docs/css-ui/animate-to-height-auto#animate_the_details_element

@supports (interpolate-size: allow-keywords) {
    :root {
        interpolate-size: allow-keywords;
    }
    
    details {
        transition: height 0.5s ease;
        height: 2.5rem;
        
        &[open] {
            height: auto;
            overflow: clip; /* Recorta el contenido mientras anima */
        }
    }
}