¿Te resulta más fácil leer con fondo crema, un tono de color, más contraste o un texto más grande? Elige tus colores y tu texto una sola vez, para todas las páginas.

Lección gratuita

Async, await y respuestas en flujo

Después de esta lección: Puedo decir por qué se espera una llamada lenta y cómo llega una respuesta en flujo.

Lección de 60 min3 comandosLaboratorio de agente de programación con IA: usa opencode para explicar y corregir código, y comprueba su trabajo

Una llamada lenta no debe bloquear nada

Una llamada a un modelo puede tardar varios segundos. Tu programa no debe quedarse bloqueado mientras espera. El código asíncrono le permite esperar sin bloquearse.

El flujo va un paso más allá. La respuesta llega por trozos, y quien la usa puede empezar a leer enseguida.

Al final de esta lección

Nota

Puedo decir por qué se espera una llamada lenta y cómo llega una respuesta en flujo.

Palabras clave

PalabraQué significa
Promesaun sustituto de un resultado que aún no ha llegado
Awaitpausar una función hasta que una promesa tenga su resultado
Función asyncuna función que puede usar await
Flujouna respuesta enviada por trozos a medida que se produce
Fragmentoun trozo de una respuesta en flujo

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Una llamada lenta devuelve una promesa

Cuando lanzas un trabajo lento, la llamada vuelve enseguida con una promesa. El resultado aún no está. Tu programa sigue con la línea siguiente.

Cuando llega el resultado, la promesa lo entrega. El orden importa: las líneas posteriores a la llamada pueden ejecutarse antes que el resultado.

Mira el orden en que pasan las cosas

Comando
docker exec -w /home/node/project agent node -e "const wait=(ms,t)=>new Promise(r=>setTimeout(()=>r(t),ms));console.log('1 inicio');wait(300,'3 la llamada lenta ha terminado').then(console.log);console.log('2 el programa sigue')"

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Await pausa una función, no el programa

Dentro de una función async, await pausa esa función hasta que la promesa tiene su resultado. El resto del programa no se bloquea.

Dos llamadas lentas que no dependen una de otra pueden salir juntas con Promise.all. Tardan más o menos lo que la más lenta, no la suma de las dos.

Await una a una, luego a la vez

Comando
docker exec -w /home/node/project agent node -e "const wait=(ms,t)=>new Promise(r=>setTimeout(()=>r(t),ms));const tick=(t0)=>Math.round((Date.now()-t0)/100)*100;(async()=>{let t0=Date.now();await wait(200,'a');await wait(200,'b');console.log('una tras otra: unos '+tick(t0)+' ms');t0=Date.now();await Promise.all([wait(200,'a'),wait(200,'b')]);console.log('a la vez: unos '+tick(t0)+' ms')})()"

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Una respuesta en flujo llega en fragmentos

Si pides stream: true, el servicio envía la respuesta a medida que la produce. La mayoría de las API de modelos usan eventos enviados por el servidor: líneas que empiezan por data:, cada una con un trocito de texto.

La última línea es data: [DONE]. Une los trozos en orden para obtener la respuesta entera.

Ejemplo resuelto: un flujo, línea a línea

Línea recibidaQué da
data: {"choices":[{"delta":{"content":"Ho"}}]}Ho
data: {"choices":[{"delta":{"content":"la"}}]}la
data: [DONE]El final, sin texto
UnidoHola

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Lee una respuesta en flujo

Comando
docker exec -w /home/node/project agent node -e "const u=(process.env.AI_URL||'http://ai:4000')+'/v1/chat/completions';(async()=>{try{const r=await fetch(u,{method:'POST',headers:{'content-type':'application/json',authorization:'Bearer lab'},body:JSON.stringify({model:'lab',messages:[{role:'user',content:'Cuenta de uno a cinco con palabras.'}],max_tokens:40,stream:true})});if(r.ok===false)throw new Error(r.status);const d=new TextDecoder();let b='',n=0,t='';for await(const p of r.body){b+=d.decode(p,{stream:true});const L=b.split(String.fromCharCode(10));b=L.pop();for(const l of L){const x=l.slice(6);if(l.startsWith('data: ')&&(x==='[DONE]')===false){const c=JSON.parse(x).choices?.[0]?.delta?.content;if(c){n++;t+=c}}}}console.log('Fragmentos con texto: '+n);console.log('Texto unido: '+t)}catch(e){console.log('Sin flujo ('+e.message+'). Muestra grabada:');console.log('Fragmentos con texto: 5');console.log('Texto unido: uno, dos, tres, cuatro, cinco')}})()"

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Un error frecuente

Advertencia

Mucha gente cree que el flujo hace el modelo más rápido. La respuesta entera tarda más o menos lo mismo. Ves las primeras palabras antes, y por eso parece más rápido. Un flujo también puede cortarse a medias: comprueba que llegó la marca de final.

Sobre este laboratorio y su código: Laboratorio de agente de programación con IA: usa opencode para explicar y corregir código, y comprueba su trabajo

¿Enseñas a una clase? Asigna este módulo a tus alumnos con guía del profesor, tareas con corrección automática y un laboratorio para cada alumno.