3. Teori: Cara Kerja AsyncSequence dan Operator Pipeline
3. Teori: Cara Kerja AsyncSequence dan Operator Pipeline
Pull-based Model dan Back-pressure
AsyncSequence menggunakan model pull-based — consumer meminta elemen berikutnya dengan await next(), dan producer tidak menghasilkan elemen baru sampai diminta. Ini berbeda dari Combine/RxSwift yang push-based.
Back-pressure terjadi secara alami: jika consumer lambat memproses, producer akan tertahan di send() / yield() sampai consumer siap. Tidak ada overflow buffer secara default.
Consumer (for await): Producer (AsyncStream/Channel):
| |
|─── next() ──────────────────►| consumer minta elemen
| |─── hasilkan elemen ───►|
|◄── elemen ────────────────── |
| |
|─── [memproses elemen] ───────| consumer sibuk, producer menunggu
| |
|─── next() ──────────────────►| consumer siap lagi
Cara Operator Bekerja
Setiap operator di Swift Async Algorithms adalah tipe yang conform ke AsyncSequence. Operator tidak menjalankan kode apapun sampai ada for await in — ini adalah lazy evaluation.
chain(sequenceA, sequenceB)
│
└── adalah struct ChainSequence<A, B>: AsyncSequence
iteratornya akan:
1. iterate A sampai selesai
2. baru iterate B
Ketika beberapa operator dirantai, terbentuk tree dari struct AsyncSequence yang saling membungkus — tidak ada data yang mengalir sampai consumer memulai iterasi.
AsyncChannel vs AsyncStream
| Tipe | Cocok Untuk | Pengirim |
|---|---|---|
AsyncStream |
Producer tunggal, tidak perlu await saat send | Satu closure |
AsyncChannel |
Multiple sender, back-pressure aware | Multiple Task via await send() |
AsyncThrowingChannel |
Seperti AsyncChannel + bisa throw error | Multiple Task |
AsyncChannel dari package ini berbeda dari AsyncStream bawaan Swift — AsyncChannel.send() adalah async dan akan menunggu jika consumer belum siap (back-pressure). AsyncStream.yield() tidak menunggu.
Clock Protocol
Operator time-based (debounce, throttle, chunked by duration) menggunakan Clock protocol dari Swift 5.7. Ini memungkinkan testing deterministik dengan MockClock tanpa menunggu waktu nyata.
// Produksi: gunakan ContinuousClock (default)
let debounced = stream.debounce(for: .milliseconds(300))
// Testing: inject MockClock untuk kontrol waktu
let clock = TestClock()
let debounced = stream.debounce(for: .milliseconds(300), clock: clock)
await clock.advance(by: .milliseconds(300)) // simulasi waktu berlalu