> ## Documentation Index
> Fetch the complete documentation index at: https://docs.markpdf.tech/llms.txt
> Use this file to discover all available pages before exploring further.

# Streaming y trabajos asincrónicos

> Consuma streaming y trabajos 202 con MarkpdfService.

# Streaming y trabajos asincrónicos

## Transmitiendo con `convertStream`

```ts theme={null}
import { Component, inject, signal } from "@angular/core";
import { MarkpdfService } from "@markpdf/angular";

@Component({
  selector: "app-stream-viewer",
  standalone: true,
  template: `<pre>{{ text() }}</pre>`,
})
export class StreamViewerComponent {
  private markpdf = inject(MarkpdfService);
  text = signal("");

  loadFromUrl(url: string) {
    this.text.set("");
    this.markpdf.convertStream({ url }).subscribe({
      next: (chunk) => this.text.update((t) => t + chunk),
      error: (err) => console.error(err),
      complete: () => console.log("stream terminado"),
    });
  }
}
```

`convertStream` devuelve un `Observable<string>` que emite un valor para cada fragmento; combínelo con `scan` de RxJS si prefiere acumular texto de forma reactiva en lugar de `signal.update`:

```ts theme={null}
import { scan } from "rxjs/operators";

markdown$ = this.markpdf
  .convertStream({ url })
  .pipe(scan((acc, chunk) => acc + chunk, ""));
```

```markup theme={null}
<pre>{{ markdown$ | async }}</pre>
```

<Note>
  `convertStream` requiere que su servidor proxy reenvíe la transmisión sin almacenamiento en búfer (consulte la [Guía del marco](/docs/public/es/sdks/angular/framework-guide)). Si su servidor acumula la respuesta completa antes de reenviarla, perderá el beneficio de latencia de la transmisión incluso si `Observable` todavía está funcionando (recibirá todo en un solo `next`).
</Note>

## Empleos por saturación (202)

Cuando todos los servidores de API están ocupados, la solicitud se pone en cola y `202` responde con un `job_id`. Consulte [`GET /jobs/{id}`](/docs/public/es/api/jobs). La forma en que lo maneje depende de si su proxy backend realiza el sondeo por usted o lo delega en el frontend.

### Encuesta delegado al backend (recomendado)

Si su servidor proxy usa SDK de Node.js/Python/etc. con `autoPoll: true` (el valor predeterminado en esos SDK), Angular nunca ve el `202`; la solicitud HTTP a su backend simplemente demora más y `convertFile`/`convertFromUrl` se resuelve hasta el resultado final.

### Encuesta manual desde Angular

Si su backend reenvía el `202` tal como está (por ejemplo, para no bloquear una conexión HTTP larga), maneje la encuesta en el componente:

```ts theme={null}
import { catchError, delay, expand, takeWhile } from "rxjs/operators";
import { of, throwError } from "rxjs";

pollJob(jobId: string) {
  return this.markpdf.getJob(jobId).pipe(
    expand((status) =>
      status.status === "queued" || status.status === "processing"
        ? this.markpdf.getJob(jobId).pipe(delay(5000))
        : of(status).pipe(takeWhile(() => false, true))
    ),
    takeWhile((status) => status.status !== "completed" && status.status !== "failed", true)
  );
}
```

```ts theme={null}
this.markpdf.convertFile(file).subscribe({
  next: (result) => {
    if (typeof result === "object" && "jobId" in result) {
      this.pollJob(result.jobId).subscribe((status) => {
        if (status.status === "completed") this.markdown.set(status.body as string);
        if (status.status === "failed") console.error(status.error);
      });
    } else {
      this.markdown.set(result as string);
    }
  },
});
```

<Tip>
  En condiciones de carga normales, no verá `202`; solo sucede cuando todos los backends están saturados. Si su backend utiliza cualquiera de los otros SDK oficiales con sondeo automático (el comportamiento predeterminado), no necesita implementar el sondeo manual en Angular en absoluto.
</Tip>

<Warning>
  Los resultados de los trabajos completados caducan en aproximadamente 1 hora. Si guarda un `jobId` y `getJob` devuelve `404`, reenvíe la conversión original.
</Warning>
