Javascript

Kebangkitan Continuation-Passing Style (CPS) dalam Orkestrasi Async Modern JavaScript

Kholil · 30 Apr 2026 · 5 min read · 1 views
Kebangkitan Continuation-Passing Style (CPS) dalam Orkestrasi Async Modern JavaScript

Continuation-Passing Style mengalami kebangkitan dalam orkestrasi async modern JavaScript, menawarkan performance superior dan kontrol granular dibanding Promises.

Ketika berbicara tentang asynchronous programming di JavaScript, mayoritas developer langsung memikirkan Promises dan async/await. Namun, ada sebuah teknik yang jauh lebih tua namun tetap relevan yang sedang mengalami kebangkitan—Continuation-Passing Style (CPS). Teknik ini bukan hanya bagian dari sejarah JavaScript, melainkan fondasi konseptual yang masih kuat untuk menangani flow control dan orchestration di era modern.

Apa Itu Continuation-Passing Style?

CPS adalah paradigma pemrograman di mana alih-alih mengembalikan nilai secara langsung, sebuah fungsi menerima "continuation"—sebuah callback yang akan menangani hasil atau error. Konsep ini terasa asing bagi developer modern, tetapi jika Anda pernah menggunakan callback functions, Anda sudah mempraktikkan bentuk sederhana dari CPS.

Secara teknis, dalam CPS, setiap fungsi yang melakukan operasi asynchronous menerima fungsi lanjutan (continuation function) sebagai argumen. Continuation ini bertanggung jawab untuk memproses hasil dan meneruskan eksekusi ke langkah berikutnya. Pendekatan ini memberikan kontrol eksplisit atas alur program.

Mari kita lihat contoh dasar:

// CPS tradisional
function fetchData(url, continuation) {
  setTimeout(() => {
    const data = { id: 1, name: 'John' };
    continuation(null, data);
  }, 1000);
}

fetchData('/api/user', (err, data) => {
  if (err) {
    console.error('Error:', err);
  } else {
    console.log('Data:', data);
  }
});

Mengapa CPS Kembali Relevan?

Meskipun Promises dan async/await telah mendominasi landscape asynchronous JavaScript selama bertahun-tahun, CPS mulai dilihat kembali dalam konteks-konteks spesifik. Ada beberapa alasan mengapa resurgence ini terjadi:

  • Performance yang dapat diprediksi: CPS tidak memiliki overhead dari Promise microtask queue, sehingga dalam aplikasi dengan volume operasi asynchronous yang sangat tinggi, CPS bisa lebih efisien.
  • Memory footprint yang lebih kecil: Setiap Promise menciptakan object di heap. Untuk operasi yang sangat frequent, ini bisa menjadi bottleneck.
  • Kontrol granular: CPS memberikan kontrol eksplisit atas execution flow, yang berguna dalam library orchestration dan middleware.
  • Kompatibilitas dengan functional programming: CPS align dengan functional programming paradigm, terutama dalam konteks composition dan higher-order functions.

CPS dalam Praktik Modern: Callback Hell dan Solusinya

Tantangan historis CPS adalah "callback hell" atau "pyramid of doom", di mana nested callbacks membuat kode sulit dibaca. Namun, pattern modern telah mengatasinya. Berikut adalah pendekatan yang lebih sophisticated:

// Menggunakan named functions untuk menghindari callback hell
function getUserData(userId, onSuccess, onError) {
  fetchUser(userId, (err, user) => {
    if (err) return onError(err);
    
    fetchUserPosts(user.id, (err, posts) => {
      if (err) return onError(err);
      
      onSuccess({ user, posts });
    });
  });
}

// Konsumsi dengan error handling yang jelas
getUserData(
  123,
  (data) => console.log('Success:', data),
  (err) => console.error('Failed:', err)
);

CPS dalam Orkestrasi Async: Node.js Streams dan Events

Salah satu area di mana CPS truly shine adalah dalam Node.js streams dan event emitters. Streams adalah implementasi natural dari CPS—data mengalir melalui pipeline dengan callbacks menangani setiap chunk.

// CPS dalam konteks Node.js streams
const fs = require('fs');

function processLargeFile(filePath, onChunk, onComplete, onError) {
  const stream = fs.createReadStream(filePath, { encoding: 'utf8' });
  
  stream.on('data', (chunk) => {
    onChunk(chunk);
  });
  
  stream.on('end', () => {
    onComplete();
  });
  
  stream.on('error', (err) => {
    onError(err);
  });
}

// Penggunaan
processLargeFile(
  '/path/to/large/file.txt',
  (chunk) => console.log('Chunk size:', chunk.length),
  () => console.log('File processed'),
  (err) => console.error('Stream error:', err)
);

CPS Composition: Membangun Middleware dan Orchestrators

Teknik powerful dalam CPS adalah composition—menggabungkan multiple continuations untuk membuat complex workflows. Ini terutama berguna dalam framework middleware.

// Compose multiple CPS functions
function composeCPS(...handlers) {
  return function execute(initialValue, finalContinuation) {
    let index = 0;
    
    function next(err, value) {
      if (err) return finalContinuation(err);
      if (index >= handlers.length) return finalContinuation(null, value);
      
      const handler = handlers[index++];
      handler(value, next);
    }
    
    next(null, initialValue);
  };
}

// Definisi handlers
const validateInput = (value, next) => {
  if (!value || value.length === 0) {
    return next(new Error('Invalid input'));
  }
  next(null, value.trim());
};

const transformData = (value, next) => {
  const transformed = value.toUpperCase();
  next(null, transformed);
};

const saveData = (value, next) => {
  console.log('Saving:', value);
  next(null, { saved: true, data: value });
};

// Compose dan execute
const pipeline = composeCPS(validateInput, transformData, saveData);

pipeline('  hello world  ', (err, result) => {
  if (err) console.error(err);
  else console.log('Result:', result);
});

Perbandingan: CPS vs Promises vs Async/Await

Untuk memberikan perspektif yang seimbang, mari kita lihat tiga pendekatan menangani operasi async yang sama:

// 1. CPS
function fetchWithCPS(url, onSuccess, onError) {
  setTimeout(() => {
    if (url) onSuccess({ data: 'result' });
    else onError(new Error('Invalid URL'));
  }, 100);
}

// 2. Promises
function fetchWithPromise(url) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (url) resolve({ data: 'result' });
      else reject(new Error('Invalid URL'));
    }, 100);
  });
}

// 3. Async/Await
async function fetchWithAsync(url) {
  await new Promise(r => setTimeout(r, 100));
  if (url) return { data: 'result' };
  throw new Error('Invalid URL');
}

// Penggunaan
fetchWithCPS('/api', 
  (data) => console.log(data),
  (err) => console.error(err)
);

fetchWithPromise('/api')
  .then(data => console.log(data))
  .catch(err => console.error(err));

await fetchWithAsync('/api');

Use Cases Kontemporer untuk CPS

Meskipun async/await lebih populer, ada beberapa skenario di mana CPS masih unggul:

  • High-frequency trading systems: Aplikasi yang membutuhkan latency ultra-rendah dan predictable performance.
  • Real-time data streaming: Processing streams dengan custom backpressure handling.
  • Resource-constrained environments: Embedded systems atau serverless functions dengan memory terbatas.
  • Complex middleware pipelines: Framework yang perlu kontrol granular atas execution flow.
  • Distributed systems orchestration: Mengoordinasikan multiple async operations dengan error recovery yang sophisticated.

Best Practices dalam CPS Modern

Jika Anda memutuskan untuk menggunakan CPS, berikut adalah beberapa best practices:

  1. Konsistensi error handling: Selalu gunakan convention "error-first callbacks" untuk consistency.
  2. Named functions: Hindari anonymous functions yang membuat call stack sulit dipahami.
  3. Type checking: Gunakan TypeScript untuk memberikan type safety pada continuations.
  4. Documentation: Dokumentasikan dengan jelas callback signature dan parameter semantics.
  5. Hybrid approach: Kombinasikan CPS dengan Promises ketika sesuai untuk mendapatkan yang terbaik dari kedua dunia.

Kesimpulan

Continuation-Passing Style bukan sekadar artifact dari masa lalu JavaScript. Dalam konteks orkestrasi async modern, terutama dalam aplikasi yang membutuhkan performance tinggi, kontrol granular, atau resource efficiency, CPS tetap menjadi tool yang valuable. Sementara async/await adalah default choice untuk majority use cases, pemahaman mendalam tentang CPS membuat Anda developer yang lebih versatile dan memberi Anda opsi ketika standardized abstraction tidak cukup optimal.

Resurgence CPS bukan tentang menggantikan Promises atau async/await, tetapi tentang mengakui bahwa JavaScript ecosystem cukup matang untuk mengakomodasi multiple paradigm, dan masing-masing memiliki tempat yang legitimate dalam toolkit modern developer.