wandres.dev
DERIVACIONES ASYNC · createAsync y el futuro

El futuro: async transparente en Solid 2.0

Qué significa que Solid 2.0 lleve lo asíncrono al núcleo del sistema reactivo: derivaciones que pueden ser async, un estado pendiente que se propaga por el grafo como ciudadano de primera clase y aflora en las fronteras Suspense y ErrorBoundary, y createAsync como el puente compatible hacia ese modelo. El giro mental de orquestar estados de carga y error a mano hacia escribir derivaciones que se leen como síncronas, y qué de tu código de hoy sobrevive intacto al salto.

⏱ 16 min

createAsync no es un destino: es una avanzadilla. Vive hoy en Solid Router porque anticipa un cambio que Solid 2.0 lleva al mismísimo núcleo del sistema reactivo —hacer lo asíncrono transparente—. La idea, tan simple de enunciar como profunda en consecuencias, es que una derivación pueda ser asíncrona sin dejar de comportarse como una derivación: la lees como si el valor estuviera ahí, y el sistema se encarga de esperar, de propagar lo pendiente por el grafo y de aflorarlo en las fronteras que ya conoces. Esta lección mira hacia dónde va Solid, qué cambia en tu forma de escribir datos y —lo más tranquilizador— cuánto de lo que aprendiste en este nivel sobrevive intacto al salto.

🎯 Al terminar esta lección sabrás
  • Entender qué significa “async transparente”: derivaciones async con lo pendiente como estado de primera clase.
  • Ver cómo ese estado pendiente se propaga por el grafo y aflora en Suspense y ErrorBoundary.
  • Reconocer createAsync como el puente compatible hacia el modelo de Solid 2.0.
  • Reescribir tu mentalidad: de orquestar carga y error a declarar derivaciones que se leen síncronas.

Qué significa async transparente

Hoy, lo asíncrono es una isla dentro del grafo reactivo: un createResource o un createAsync marcan la frontera donde lo síncrono se encuentra con lo que tarda, y el resto del grafo —memos, efectos— vive en tierra firme, sin saber esperar. Solid 2.0 borra esa frontera. La ambición es que cualquier derivación pueda ser asíncrona: un createMemo podrá devolver una promesa, leer otra derivación que aún no resolvió, y el sistema entenderá que el resultado está pendiente sin que tú montes ninguna maquinaria.

La pieza clave es que lo pendiente deja de ser un booleano que administras y pasa a ser un estado que el grafo transporta solo. Cuando una derivación depende de un valor que aún no llegó, ella misma queda pendiente; y esa condición se propaga hacia arriba por las dependencias, exactamente igual que hoy se propaga un cambio de valor. El grafo ya sabía difundir “esto cambió”; en 2.0 sabe además difundir “esto todavía no está”.

// El modelo hacia el que se avanza: derivaciones que se encadenan
// leyendose como sincronas, y el sistema gestiona la espera.
const usuario = createAsync(() => getUsuario(id()));
const inicial = () => usuario().nombre[0];   // se lee como sincrono
const saludo  = () => `Hola, ${usuario().nombre}`;
// nada de esto comprueba "cargando": lo pendiente fluye por el grafo

La consecuencia práctica es que las capas intermedias se evaporan. Hoy, entre la petición y la pantalla se interponen un recurso, un puñado de comprobaciones de loading y varios guardias contra el undefined; cada capa es código que escribes, pruebas y mantienes. En el modelo transparente esas capas desaparecen porque el grafo hace el trabajo: la derivación inicial ni sabe ni necesita saber que su fuente es asíncrona, y aun así quedará pendiente en cuanto lo esté usuario, arrastrando esa condición hacia la frontera sin una línea tuya de por medio.

El estado pendiente aflora en las fronteras

Si lo pendiente se propaga solo, ¿dónde se detiene y se hace visible? En las fronteras que ya usas. Cuando una porción del árbol depende de una derivación pendiente, esa condición sube por el grafo hasta el Suspense más cercano, que muestra su fallback; si en vez de tardar la promesa rechaza, el estado de error sube hasta el ErrorBoundary. No tienes que cablear nada: declaras las fronteras donde quieres que la carga y el fallo se materialicen, y el grafo enruta cada estado transitorio hasta ellas.

Esto ya lo viviste con createAsync en las lecciones anteriores, y esa es justo la señal de que vas por delante: el modelo de Solid 2.0 no es una API nueva que aprender, sino la generalización a todo el grafo de lo que hoy hace un primitivo del router. Lo que en 2026 es el comportamiento de createAsync —leer llamando, suspender hacia una frontera, rastrear como un memo— en 2.0 será el comportamiento de la reactividad a secas.

Hay una elegancia estructural en esto que conviene no pasar por alto: la misma frontera Suspense que hoy coordina la carga de tus datos coordinará la de cualquier derivación intermedia, sin que tengas que anticipar cuáles serán asíncronas. Colocas la frontera pensando en la experiencia —qué región debe mostrar un indicador mientras algo de su interior no está listo— y el grafo dirige hasta ella cualquier pendiente que surja debajo, hoy o tras un refactor futuro que vuelva async una derivación que antes no lo era.

// una frontera unica recoge lo pendiente de todo lo que hay debajo,
// sin importar cuantas derivaciones intermedias resulten ser async
<ErrorBoundary fallback={(e) => <Fallo err={e} />}>
  <Suspense fallback={<Cargando />}>
    <Panel />{/* si cualquier derivacion interior queda pendiente, cae aqui */}
  </Suspense>
</ErrorBoundary>
ℹ️
createResource no muere: se queda para cuando quieras los mandos

El async transparente no borra createResource. El recurso seguirá siendo la herramienta cuando necesites control imperativo explícito —mutate optimista, refetch puntual— o cuando quieras modelar la petición como un objeto con estado que administras. Lo que cambia es el caso por defecto: leer estado del servidor dejará de empezar por “monto un recurso y compruebo loading” para empezar por “escribo una derivación y declaro una frontera”. El recurso pasa de norma a excepción especializada, igual que en el mundo síncrono createMemo es la norma y los efectos manuales la excepción.

Qué cambia en tu forma de escribir datos

El giro es mental antes que sintáctico. Durante años, escribir datos asíncronos significó orquestar estados: declarar cargando, declarar error, encadenar peticiones con banderas, sembrar la interfaz de guardias defensivos contra el undefined. El async transparente jubila ese oficio. Escribes una derivación que lee otras derivaciones como si sus valores estuvieran presentes, y delegas los estados transitorios a las fronteras. Piensas en qué depende de qué, no en cuándo llega cada cosa.

El cambio se nota en tres gestos concretos:

  • Dejas de declarar signals de cargando y error: esos estados suben a las fronteras.
  • Dejas de encadenar peticiones con banderas: encadenas leyendo un accessor dentro de otro.
  • Dejas de sembrar guardias contra el undefined: dentro de la frontera el valor siempre está.
// ANTES: orquestacion manual de estados
const [datos, setDatos] = createSignal();
const [cargando, setCargando] = createSignal(true);
const [error, setError] = createSignal();
createEffect(async () => {
  setCargando(true);
  try { setDatos(await pedir(id())); }
  catch (e) { setError(e); }
  finally { setCargando(false); }
});

// DESPUES: una derivacion que se lee como sincrona
const datos = createAsync(() => pedir(id()));
// carga -> <Suspense> ; error -> <ErrorBoundary> ; valor -> datos()

La buena noticia para tu código de hoy es que la migración ya empezó y es indolora. Todo lo que has escrito en este nivel —leer llamando, rastrear antes del await, componer sin cascadas, latest con transiciones— es exactamente el modelo de 2.0 en miniatura. Adoptar createAsync con query ahora no es apostar por una moda: es escribir el código que seguirá siendo idiomático cuando lo asíncrono baje del router al núcleo. La forma no cambiará; solo se hará universal.

Qué no tendrás que reaprender

El miedo razonable ante un cambio de versión mayor es tener que desaprender lo que ya dominas. Con el async transparente ocurre lo contrario: cuanto más idiomático sea hoy tu código con createAsync, menos notarás el salto. Repasa los hábitos de este nivel y comprobarás que cada uno es ya una pieza del modelo que viene.

  • Leer llamando en vez de desestructurar una tupla: seguirá igual, solo que para cualquier derivación y no solo las del router.
  • Rastrear antes del await: la frontera del rastreo síncrono no se mueve; el grafo asíncrono la respeta idéntica.
  • Componer leyendo un accessor dentro de otro: es exactamente cómo se encadenarán las derivaciones async en el núcleo.
  • Declarar carga y error en fronteras: Suspense y ErrorBoundary son las mismas piezas, ahora alimentadas por todo el grafo.
// este codigo de 2026 no cambia de forma cuando el async baje al nucleo
const usuario = createAsync(() => getUsuario(id()));
const amigos = createAsync(() => getAmigosDe(usuario().id)); // encadenado, se lee sincrono

// la carga sigue siendo un Suspense, el error un ErrorBoundary, la lectura una llamada
const primero = () => amigos()[0]?.nombre ?? "sin amigos";

Lo único que cambiará es de dónde importas el primitivo y cuánto abarca: lo que hoy es una capacidad de la librería de rutas, mañana será una propiedad de la reactividad misma. El gesto de tu mano sobre el teclado no se moverá una pulgada.

💡
Migrar temprano es la migración más barata

La forma más económica de prepararte para Solid 2.0 no es esperar a que llegue: es escribir hoy con createAsync y query en lugar de con createEffect más signals de estado. Cada componente que conviertas ahora es uno que no tendrás que tocar después, porque ya habla el idioma del núcleo futuro. La deuda de un modelo asíncrono orquestado a mano se paga con intereses el día del salto; el código declarativo, en cambio, lo cruza sin una sola línea de cambio.

flowchart TD
F[fuente async] --> D1[derivacion se lee como sincrona]
D1 --> D2[derivacion dependiente]
D2 -->|valor presente| UI[render]
D2 -.pendiente sube por el grafo.-> SU[Suspense muestra fallback]
D2 -.error sube por el grafo.-> EB[ErrorBoundary captura]
style D1 fill:#89b4fa,color:#11111b
style UI fill:#a6e3a1,color:#11111b
style SU fill:#f9e2af,color:#11111b
style EB fill:#f38ba8,color:#11111b
🌊

Pendiente que fluye

Lo pendiente deja de ser un booleano que administras y pasa a propagarse por el grafo como un cambio de valor mas.

🧱

Estados en fronteras

Carga y error afloran en Suspense y ErrorBoundary. Declaras donde se materializan, no como se cablean.

🌉

createAsync es el puente

Lo que hoy hace en el router sera el comportamiento del grafo en 2.0. Adoptarlo ahora es escribir codigo futuro.

🧭

createResource permanece

No desaparece: se reserva para el control imperativo, mutate optimista y refetch puntual. Pasa de norma por defecto a excepcion especializada.

El destino de la reactividad de grano fino era tragarse lo asíncrono, y por eso el cambio se siente inevitable

Vale la pena entender por qué Solid, y no otro framework, es quien lleva lo asíncrono al núcleo con esta naturalidad, porque revela algo sobre la idea entera. La reactividad de grano fino siempre fue un mecanismo para propagar cambios por un grafo de dependencias: una fuente se mueve, y la onda recorre memos y efectos actualizando solo lo que toca. Lo asíncrono, visto de cerca, no es más que otra clase de cambio —uno que llega tarde—. Un dato que aún no resolvió es una dependencia cuyo valor todavía no existe, y “esto está pendiente” es tan propagable por el grafo como “esto cambió”. Que durante años lo tratáramos como una isla aparte, con recursos y banderas de carga, fue un accidente histórico, no una necesidad: heredamos el modelo de un mundo —el de React— donde no había grafo que propagara nada y todo se resolvía re-renderizando y comprobando estados a mano. Solid, que sí tiene el grafo, puede hacer lo que aquel mundo no podía: dejar que lo pendiente viaje por las mismas vías que ya transportan los cambios, y aflore en las mismas fronteras donde ya aflora todo lo demás. Por eso el async transparente no se siente como una función más sino como el cierre de un círculo: el grano fino era, desde el principio, la arquitectura correcta para lo asíncrono, solo que tardamos en verlo. Y por eso createAsync importa tanto hoy pese a vivir todavía en una librería de rutas —es el primer lugar donde esa verdad se hizo API—. Cuando escribes una derivación async y la lees como si el valor estuviera ahí, no estás usando un truco de conveniencia: estás participando de la forma final que la reactividad quería tener. Aprenderlo ahora es dejar de gestionar lo asíncrono para empezar a declararlo, que es lo único que el grano fino, llevado hasta sus últimas consecuencias, iba a pedirte de todos modos.

⚔️ Escribe hoy el código de mañana
  1. Toma un componente que orqueste cargando, error y datos con un createEffect y tres signals, y reescríbelo como una única createAsync con Suspense y ErrorBoundary.
  2. Cuenta las líneas y los estados que eliminaste, y nombra qué preocupación se movió del componente a la frontera.
  3. Encadena dos derivaciones leyendo la primera dentro de la segunda como si fueran síncronas, y observa que no aparece ni un solo booleano de carga.
  4. Argumenta por qué “esto está pendiente” es propagable por el grafo igual que “esto cambió”, y qué implica eso para un createMemo que dependa de un valor async.
  5. Justifica, con un caso concreto, por qué createResource sobrevive al async transparente y qué le pedirías que createAsync no te da.