Future–Promise Pattern #

Dalam sistem concurrent dan asynchronous modern, tidak semua operasi dapat langsung menghasilkan nilai secara instan. Banyak proses—seperti pemanggilan API eksternal, query database berat, atau komputasi paralel—membutuhkan waktu yang tidak dapat diprediksi. Jika kita memblokir eksekusi sambil menunggu hasilnya, performa dan skalabilitas sistem akan menurun.

Di sinilah Future–Promise Pattern berperan. Pattern ini memungkinkan kita untuk mendeklarasikan hasil di masa depan dan melanjutkan eksekusi tanpa harus menunggu hasil tersebut tersedia saat itu juga.

Apa itu Future–Promise Pattern? #

Future–Promise Pattern adalah concurrency pattern yang memisahkan:

  • Promise → pihak yang menghasilkan nilai di masa depan
  • Future → pihak yang mengonsumsi nilai tersebut ketika sudah tersedia

Secara konseptual:

  • Promise bertanggung jawab untuk menyelesaikan (resolve) atau menggagalkan (reject) sebuah hasil
  • Future bertindak sebagai placeholder terhadap hasil tersebut dan menyediakan mekanisme untuk menunggu atau mengambil nilainya

Pattern ini sangat umum ditemukan pada bahasa dan framework asynchronous seperti Java (Future, CompletableFuture), JavaScript (Promise), Scala (Future), dan juga dapat diimplementasikan secara idiomatis di Go.


Tujuan Pattern #

Future–Promise Pattern memiliki beberapa tujuan utama:

  1. Non-blocking execution Memungkinkan eksekusi tetap berjalan tanpa menunggu hasil operasi yang lama.

  2. Decoupling producer & consumer Penghasil data dan pemakai data tidak perlu saling mengenal secara langsung.

  3. Improved concurrency & scalability Task dapat dieksekusi secara paralel dan hasilnya dikumpulkan di kemudian waktu.

  4. Struktur async yang lebih rapi Menghindari callback hell atau goroutine management yang berantakan.


Kapan Cocok Digunakan? #

Pattern ini cocok digunakan ketika:

  • Operasi membutuhkan waktu lama (I/O, network, heavy computation)
  • Hasil tidak dibutuhkan secara langsung
  • Terdapat banyak task asynchronous yang dapat berjalan paralel
  • Ingin memisahkan lifecycle eksekusi dan konsumsi hasil

Contoh use case:

  • Parallel HTTP requests
  • Background job processing
  • Fan-out / fan-in concurrency
  • Asynchronous pipeline

Tidak disarankan jika:

  • Operasi sangat cepat
  • Kompleksitas async tidak memberikan manfaat nyata

Contoh Implementasi (Golang) #

Go tidak memiliki keyword Future atau Promise secara eksplisit, namun goroutine + channel dapat digunakan untuk merepresentasikan pattern ini secara alami.

Contoh Sederhana Future #

package main

import (
	"fmt"
	"time"
)

// Future merepresentasikan hasil di masa depan
type Future struct {
	result chan int
}

func NewFuture(task func() int) *Future {
	f := &Future{result: make(chan int, 1)}

	go func() {
		res := task()
		f.result <- res
	}()

	return f
}

func (f *Future) Get() int {
	return <-f.result
}

func main() {
	future := NewFuture(func() int {
		time.Sleep(2 * time.Second)
		return 42
	})

	fmt.Println("Doing something else...")

	result := future.Get()
	fmt.Println("Result:", result)
}

Penjelasan #

  • NewFuture → bertindak sebagai Promise
  • Future → bertindak sebagai Future
  • Get() → memblokir hanya saat hasil benar-benar dibutuhkan

Implementasi Error Handling #

Dalam praktik nyata, Future hampir selalu membawa error.

type Result[T any] struct {
	Value T
	Err   error
}

type Future[T any] struct {
	result chan Result[T]
}

func NewFuture[T any](task func() (T, error)) *Future[T] {
	f := &Future[T]{result: make(chan Result[T], 1)}

	go func() {
		val, err := task()
		f.result <- Result[T]{Value: val, Err: err}
	}()

	return f
}

func (f *Future[T]) Get() (T, error) {
	res := <-f.result
	return res.Value, res.Err
}

Perbandingan dengan Pattern Lain #

  • Future–Promise vs Callback Future lebih terstruktur dan composable.

  • Future–Promise vs Thread Pool Thread pool fokus pada eksekusi worker, Future fokus pada hasil.

  • Future–Promise vs Async/Await Async/Await adalah sintaks, Future adalah konsep.


Best Practices #

  1. Selalu batasi lifecycle Future Kombinasikan dengan context.Context untuk cancellation & timeout.

  2. Gunakan buffer pada channel Hindari goroutine leak akibat channel yang tidak terbaca.

  3. Jangan over-engineering Jika channel sederhana sudah cukup, Future abstraction mungkin berlebihan.

  4. Handle error secara eksplisit Jangan hanya mengembalikan value tanpa error.

  5. Perhatikan fan-out berlebihan Terlalu banyak Future dapat membebani scheduler dan memory.


Penutup #

Future–Promise Pattern adalah fondasi penting dalam desain sistem concurrent dan asynchronous. Dengan memisahkan antara kapan task dijalankan dan kapan hasil digunakan, pattern ini membantu menciptakan sistem yang lebih scalable, responsif, dan mudah dipelihara.

Di Go, meskipun tidak tersedia secara eksplisit, Future–Promise dapat diimplementasikan dengan elegan menggunakan goroutine, channel, dan generics. Dengan penerapan yang tepat dan tidak berlebihan, pattern ini dapat menjadi alat yang sangat powerful dalam concurrency toolbox Anda.

“Concurrency is about dealing with lots of things at once. Future–Promise helps you deal with the results later.”

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact