Membangkitkan Middleware
Di bab sebelumnya, kami telah menjelaskan middleware sebagai lapisan-lapisan function yang saling bertumpukan. Meski tidak sepenuhnya salah, namun menyebutnya sebagai “tumpukan” atau stack hanyalah penyederhanaan supaya mudah dipahami.
Middleware di grammY
Umumnya, kamu akan menemui pola seperti ini.
const bot = new Bot("");
bot.use(/* ... */);
bot.use(/* ... */);
bot.on(/* ... */);
bot.on(/* ... */);
bot.on(/* ... */);
bot.start();
2
3
4
5
6
7
8
9
10
Memang mirip seperti tumpukan, tetapi di balik layar mereka sebenarnya lebih mirip seperti cabang pohon. Komponen utama pembentuk pohon ini adalah class Composer
(referensi).
Pertama-tama, setiap instance Bot
adalah instance dari Composer
. Ia hanyalah sebuah subclass. Oleh karena itu class Bot extends Composer
.
Kamu juga harus tahu bahwa setiap method Composer
di dalamnya memanggil use
. Sebagai contoh, filter
memanggil use
dengan beberapa percabangan middleware, sementara on
memanggil filter
lagi dengan beberapa predicate function yang terdapat kecocokan antara update dan filter query yang diberikan. Untuk saat ini, kita cukupkan pembahasan mengenai use
dan lanjut ke pembahasan berikutnya.
Kita akan mengupas sedikit mengenai apa yang dilakukan Composer
terhadap pemanggilan use
, dan apa bedanya dengan sistem middleware lain di luar sana. Perbedaannya mungkin tidak begitu mencolok, tetapi tunggu hingga kita membahas mengenai manfaat luar biasa yang dihasilkannya.
Memperbanyak Composer
Kamu bisa memasang lebih banyak middleware di instance Composer
, bahkan setelah memasang Composer
itu sendiri di tempat lain.
const bot = new Bot(""); // subclass dari `Composer`
const composer = new Composer();
bot.use(composer);
// Berikut akan dijalankan:
composer.use(/* A */);
composer.use(/* B */);
composer.use(/* C */);
2
3
4
5
6
7
8
9
A
, B
, dan C
akan dijalankan. Yang dimaksud di sini adalah setelah kamu menambahkan sebuah instance Composer
, kamu masih bisa memanggil use
pada instance tersebut dan semua middleware-nya akan tetap dijalankan. (Ini bukan sesuatu yang istimewa, tetapi sudah menjadi sebuah pembeda utama dari framework populer sejenis yang mengabaikan pemanggilan berikutnya).
Kamu mungkin bertanya-tanya dimana struktur pohonnya. Mari kita lihat potongan kode berikut:
const composer = new Composer();
composer.use(/* A */);
composer.use(/* B */).use(/* C */);
composer.use(/* D */).use(/* E */).use(/* F */).use(/* G */);
composer.use(/* H */).use(/* I */);
composer.use(/* J */).use(/* K */).use(/* L */);
2
3
4
5
6
7
Sudah terlihat?
Yup, semua middleware tersebut akan dijalankan secara berurutan dari A
ke L
.
Library lain akan menjalankan kode dengan cara composer
dan seterusnya. Sebaliknya, grammY menggunakan struktur pohon: satu simpul akar (composer
) memiliki lima cabang (A
, B
, D
, H
, J
), sementara cabang dari B
memiliki satu anak cabang, yaitu C
, dan seterusnya. Pohon ini kemudian akan dilalui oleh setiap update sesuai urutan kedalamannya, yang sama efektifnya dengan melewati A
ke L
secara berurutan, mirip yang kita kenal di sistem lain.
Proses ini memungkinkan untuk membuat instance baru dari Composer
setiap kali kamu memanggil use
, yang kemudian akan di-extend (seperti yang sudah diterangkan di atas).
Menggabungkan Pemanggilan use
Kalau kita hanya menggunakan satu use
, itu tidak akan terlalu bag-use — maaf, garing 😬. Akan semakin menarik ketika kita menambahkan sesuatu ke dalamnya, misal filter
.
Lihat kode berikut:
const composer = new Composer();
composer.filter(/* 1 */, /* A */).use(/* B */)
composer.filter(/* 2 */).use(/* C */, /* D */)
2
3
4
5
Di baris 3, kita menambahkan A
di belakang predicate function 1
. A
akan dievaluasi untuk update hanya jika kondisi 1
terpenuhi. Meski begitu, filter
mengembalikan instance Composer
yang telah kita tambahkan dengan pemanggilan use
di baris 3, sehingga B
masih terproteksi oleh 1
, meskipun ia dipasang di pemanggilan use
yang berbeda.
Baris 5 sama halnya dengan baris 3, C
dan D
hanya akan dijalankan jika kondisi 2
terpenuhi.
Masih ingat bagaimana pemanggilan bot
bisa dirangkai untuk menggabungkan beberapa filter query menggunakan AND? Sekarang, bayangkan ini:
const composer = new Composer();
composer.filter(/* 1 */).filter(/* 2 */).use(/* A */);
2
3
2
hanya akan dievaluasi jika 1
terpenuhi, dan A
hanya akan dijalankan jika 2
(serta 1
) terpenuhi.
Coba kunjungi kembali materi mengenai pengombinasian filter query dengan pengetahuan dan kekuatan barumu.
Khusus fork
sedikit berbeda karena ia memulai dua komputasi secara bersamaan, misal disisipkan ke event loop. Alih-alih mengembalikan instance Composer
yang dibuat oleh pemanggilan use
, ia mengembalikan sebuah Composer
yang merefleksikan komputasi percabangan. Dengan begitu akan memungkinkan untuk membuat pola seperti bot
. A
sekarang akan dieksekusi di percabangan komputasi paralel.