Memakai Iterator Dan Generator Di Node.js
Di JavaScript, tidak ada class khusus untuk mewakili iterator. Semua object adalah iterator bila object tersebut memiliki sebuah method dengan nama next()
yang mengembalikan sebuah object yang mengandung value
dan/atau done
. Ini disebut sebagai iterator protocol. Sebuah object disebut sebagai iterable apabila ia memiliki method dengan nama @@iterator
(Symbol.iterator
) yang mengembalikan sebuah iterator. Untuk next()
yang dikerjakan secara asynchronous (seperti async next()
), saya dapat mengganti @@iterator
menjadi @@asyncIterator
(Symbol.asyncIterator
). Object dari String
, Array
, TypedArray
, Map
dan Set
merupakan iterable. Konstruksi for...of
dan for await...of
dapat dipakai untuk melakukan iterasi pada iterable.
Sebagai contoh, berikut ini adalah iterable sekaligus iterator yang akan mengembalikan deratan Fibonacci:
Mengapa membuat object tersebut iterable juga? Tujuannya adalah supaya saya bisa menggunakan function ini di for...of
seperti:
Di JavaScript, struktur data bawaan seperti Array
, Map
, Set
, dan sebagainya mendukung sebuah iterable sebagai masukan di constructor. Sebagai contoh, bila saya ingin menampilkan deretan Fibonacci hasil dari iterable di atas dalam satu baris yang dipisahkan dengan tanda koma, saya bisa mengubahnya menjadi Array
terlebih dahulu dengan menggunakan kode program seperti:
String di JavaScript juga sebuah iterable. Saya bisa menggunakan fakta ini untuk menyelesaikan pertanyaan interview: “Buat kode program JavaScript yang membalikkan sebuah string, tetapi hanya berlaku untuk huruf (a-z). Semua karakter non-huruf seperti simbol dan angka harus tetap berada di posisi semula.” Solusi dengan menggunakan iterable akan terlihat seperti:
Dengan menggunakan Array
dan iterable, solusi di atas bahkan tidak melibatkan looping dengan for
sama sekali.
Generator function adalah sebuah function yang dideklarasikan dengan menggunakan function*
. Bila function ini dipanggil, ia akan mengembalikan iterator khusus yang disebut generator. Di deklarasi function, keyword yield
dapat dipakai untuk menghasilkan nilai baru di iterator yang dihasilkan. Tujuan dari generator adalah membuat kode program iterator menjadi lebih sederhana. Sebagai contoh, ini adalah versi fibonnaciIterator
yang menggunakan generator :
Kode program di atas terlihat lebih sederhana dibandingkan versi sebelumnya. Karena generator kompatibel dengan iterator, saya tetap dapat menggunakannya di for...of
dan tempat lain dimana iterator diharapkan, seperti:
Selain yield
, juga terdapat yield*
yang merupakan cara singkat untuk men-yield nilai dari iterator yang sudah ada satu per satu. Sebagai contoh, karena sudah ada iteratable fibonacciIterator
, saya bisa menggunakannya di fibonacciGenerator
seperti:
Di Node.js, stream juga adalah sebuah async generator dan async iterator. Dengan demikian, untuk membaca dari file dengan menggunakan Readable
stream, saya bisa menggunakan kode program seperti:
Kode program di atas akan meng-ekstrak sebuah file terkompresi menggunakan stream yang dihasilkan oleh zlib.createGunzip()
. Untuk menampilkan isi file, biasanya saya akan menggunakan callback pada event 'data'
. Akan tetapi, pada contoh di atas, saya menggunakan for await
yang terlihat sedikit lebih sederhana.
Sebaliknya, saya juga bisa menulis ke Writable
stream dari async iterator, seperti pada contoh kode program berikut ini:
Pada kode program di atas, saya menggunakan Readable.from()
untuk menghasilkan sebuah Readable
stream dari generator fibonacciGenerator
. Saya kemudian mengarahkan Readable
stream ini ke sebuah Transform
stream buatan sendiri yang akan menerjemahkan angka menjadi string karena stream dari fs
hanya bekerja dengan string atau Buffer
saja. Tujuan berikutnya adalah Gzip
stream dari zlib
yang akan melakukan kompresi data. Hasil terkompresi kemudian diarahkan ke sebuah Writable
stream yang akan menulis ke file dengan nama output.gz
. Bila saya membuka dan melakukan dekrompresi berkas tersebut, saya akan menemukan 1.000 deretan Fibonacci pertama di file output
.