Perilaku generator yang dijelaskan di artikel sebelumnya tidak rumit, tetapi sangat mengejutkan dan mungkin tampak membingungkan pada awalnya. Jadi, alih-alih mempelajari konsep baru, sekarang kita akan berhenti sejenak dan melihat contoh menarik dalam menggunakan generator.
Mari kita memiliki fungsi seperti ini:
function maybeAddNumbers() {
const a = maybeGetNumberA();
const b = maybeGetNumberB();
return a + b;
}
Berfungsi
maybeGetNumberA
dan
maybeGetNumberB
mengembalikan angka, tetapi terkadang bisa kembali
null
atau
undefined
. Ini dibuktikan dengan kata "mungkin" di nama mereka. Jika ini terjadi, jangan mencoba memasukkan nilai-nilai ini (misalnya, angka dan
null
), lebih baik berhenti dan kembali, katakanlah
null
. Yaitu
null
, dan bukan beberapa nilai tak terduga yang diperoleh dengan menambahkan
null
/
undefined
dengan angka atau lainnya
null
/
undefined
.
Jadi, Anda perlu memeriksa apakah angkanya benar-benar ditentukan:
function maybeAddNumbers() {
const a = maybeGetNumberA();
const b = maybeGetNumberB();
if (a === null || a === undefined || b === null || b === undefined) {
return null;
}
return a + b;
}
Semuanya bekerja, tetapi jika
a
ini
null
atau
undefined
, maka tidak ada gunanya memanggil fungsi
maybeGetNumberB
. Kami tahu bahwa itu akan dikembalikan
null
.
Mari tulis ulang fungsinya:
function maybeAddNumbers() {
const a = maybeGetNumberA();
if (a === null || a === undefined) {
return null;
}
const b = maybeGetNumberB();
if (b === null || b === undefined) {
return null;
}
return a + b;
}
Begitu. Alih-alih tiga baris kode sederhana, kami dengan cepat membengkaknya menjadi 10 baris (tidak termasuk yang kosong). Dan fungsi sekarang diterapkan
if
, yang harus Anda pelajari untuk memahami apa fungsinya. Dan ini hanyalah contoh pendidikan! Bayangkan basis kode nyata dengan logika yang jauh lebih kompleks, membuat pemeriksaan seperti itu semakin sulit. Saya ingin menggunakan generator di sini dan menyederhanakan kodenya.
Lihatlah:
function* maybeAddNumbers() {
const a = yield maybeGetNumberA();
const b = yield maybeGetNumberB();
return a + b;
}
Bagaimana jika kita bisa membiarkan ekspresi
yield <smething>
menguji apakah itu
<smething>
nilai nyata, dan bukan
null
atau
undefined
? Jika ternyata bukan angka, maka kita tinggal berhenti dan kembali
null
, seperti pada kode versi sebelumnya.
Artinya, Anda dapat menulis kode yang terlihat seperti hanya bekerja dengan nyata, nilai yang ditetapkan. Generator dapat memeriksa ini dan mengambil tindakan yang sesuai untuk Anda! Ajaib, bukan? Dan itu tidak hanya mungkin, itu mudah untuk ditulis!
Tentu saja, generator itu sendiri tidak memiliki fungsi ini. Mereka hanya mengembalikan iterator, dan Anda dapat memasukkan nilai kembali ke generator jika Anda mau. Jadi kita perlu menulis pembungkus, biarlah
runMaybe
.
Daripada memanggil fungsi secara langsung:
const result = maybeAddNumbers();
kami akan menyebutnya sebagai argumen pembungkus:
const result = runMaybe(maybeAddNumbers());
Pola ini sangat umum terjadi pada generator. Sendiri, mereka tidak tahu banyak, tetapi dengan bantuan pembungkus yang ditulis sendiri, Anda dapat memberi generator perilaku yang diinginkan! Inilah yang kita butuhkan sekarang.
runMaybe
- fungsi yang mengambil satu argumen: iterator yang dibuat oleh generator:
function runMaybe(iterator) {
}
Mari kita jalankan iterator ini dalam satu lingkaran
while
. Untuk melakukan ini, Anda perlu memanggil iterator untuk pertama kalinya dan mulai memeriksa propertinya
done
:
function runMaybe(iterator) {
let result = iterator.next();
while(!result.done) {
}
}
Di dalam loop, kami memiliki dua kemungkinan. Jika
result.value
adalah
null
atau
undefined
, maka iterasi harus segera dihentikan dan dikembalikan
null
. Mari kita lakukan:
function runMaybe(iterator) {
let result = iterator.next();
while(!result.done) {
if (result.value === null || result.value === undefined) {
return null;
}
}
}
Di sini, kami
return
segera menghentikan iterasi dengan bantuan dan kembali dari pembungkusnya
null
. Tetapi jika itu
result.value
adalah angka, maka Anda perlu "kembali" ke generator. Misalnya, jika
yield maybeGetNumberA()
fungsinya
maybeGetNumberA()
adalah angka, maka Anda perlu mengganti
yield maybeGetNumberA()
nilai angka tersebut. Biar saya jelaskan: misalkan hasil perhitungannya
maybeGetNumberA()
adalah 5, lalu kita ganti
const a = yield maybeGetNumberA();
dengan
const a = 5;
. Seperti yang Anda lihat, kami tidak perlu mengubah nilai yang diekstraksi, itu cukup untuk meneruskannya kembali ke generator.
Kami ingat bahwa Anda dapat mengganti
yield <smething>
dengan beberapa nilai dengan meneruskannya sebagai argumen ke metode
next
di iterator:
function runMaybe(iterator) {
let result = iterator.next();
while(!result.done) {
if (result.value === null || result.value === undefined) {
return null;
}
// we are passing result.value back
// to the generator
result = iterator.next(result.value)
}
}
Seperti yang Anda lihat, hasil baru sekarang disimpan dalam variabel lagi
result
. Ini dimungkinkan karena kami secara khusus menyatakan
result
menggunakan
let
.
Sekarang, jika generator menemui a
null
/ saat mengambil nilai
undefined
, kita cukup kembali
null
dari wrapper
runMaybe
.
Tetap menambahkan sesuatu yang lain sehingga proses iterasi berakhir tanpa mendeteksi
null
/
undefined
. Lagi pula, jika kita mendapatkan dua angka, maka kita perlu mengembalikan jumlahnya dari pembungkus!
Generator
maybeAddNumbers
diakhiri dengan ekspresi
return
. Kami memahami kehadiran itu
return <smething>
di generator menyebabkannya mengembalikan
next
objek dari panggilan
{ value: <smething>, done: true }
. Ketika ini terjadi, pengulangan
while
berhenti karena properti
done
mendapat nilai
true
. Tetapi nilai terakhir yang dikembalikan (dalam kasus khusus kami, ini
a + b
) akan tetap disimpan di properti
result.value
! Dan kita bisa mengembalikannya:
function runMaybe(iterator) {
let result = iterator.next();
while(!result.done) {
if (result.value === null || result.value === undefined) {
return null;
}
result = iterator.next(result.value)
}
// just return the last value
// after the iterator is done
return result.value;
}
Dan itu semua!
Mari buat fungsi
maybeGetNumberA
dan
maybeGetNumberB
, dan biarkan mereka mengembalikan bilangan real terlebih dahulu:
const maybeGetNumberA = () => 5;
const maybeGetNumberB = () => 10;
Mari jalankan kode dan catat hasilnya:
function* maybeAddNumbers() {
const a = yield maybeGetNumberA();
const b = yield maybeGetNumberB();
return a + b;
}
const result = runMaybe(maybeAddNumbers());
console.log(result);
Seperti yang diharapkan, angka 15 akan muncul di konsol.
Sekarang, ganti salah satu istilah dengan
null
:
const maybeGetNumberA = () => null;
const maybeGetNumberB = () => 10;
Saat menjalankan kode, kami mendapatkan
null
!
Namun, penting bagi kita untuk memastikan bahwa fungsi tersebut
maybeGetNumberB
tidak dipanggil jika
maybeGetNumberA
mengembalikan
null
/
undefined
. Mari kita periksa kembali apakah perhitungannya berhasil. Untuk melakukan ini, tambahkan saja ke fungsi kedua
console.log
:
const maybeGetNumberA = () => null;
const maybeGetNumberB = () => {
console.log('B');
return 10;
}
Jika kita sudah menulis wrapper dengan benar
runMaybe
, maka ketika kode ini dijalankan, huruf tersebut
B
tidak akan muncul di konsol.
Memang saat mengeksekusi kodenya, kita akan melihat secara sederhana
null
. Ini berarti pembungkus benar-benar menghentikan generator segera setelah mendeteksi
null
/
undefined
.
Kode berfungsi sebagaimana mestinya: menghasilkan
null
kombinasi apa pun:
const maybeGetNumberA = () => undefined;
const maybeGetNumberB = () => 10;
const maybeGetNumberA = () => 5;
const maybeGetNumberB = () => null;
const maybeGetNumberA = () => undefined;
const maybeGetNumberB = () => null;
Dll
Tetapi manfaat dari contoh ini tidak terletak pada eksekusi kode khusus ini. Itu terletak pada fakta bahwa kami telah membuat pembungkus universal yang dapat bekerja dengan generator apa pun yang dapat mengekstrak nilai
null
/
undefined
.
Mari tulis fungsi yang lebih kompleks:
function* maybeAddFiveNumbers() {
const a = yield maybeGetNumberA();
const b = yield maybeGetNumberB();
const c = yield maybeGetNumberC();
const d = yield maybeGetNumberD();
const e = yield maybeGetNumberE();
return a + b + c + d + e;
}
Anda dapat melakukannya di bungkus kami tanpa masalah
runMaybe
! Faktanya, tidak masalah bagi pembungkus bahwa fungsi kita mengembalikan angka. Bagaimanapun, kami tidak menyebutkan tipe numerik di dalamnya. Jadi, Anda dapat menggunakan nilai apa pun di generator - angka, string, objek, array, struktur data yang lebih kompleks - dan ini akan berfungsi dengan pembungkus kami!
Inilah yang menginspirasi pengembang. Generator memungkinkan Anda menambahkan fungsionalitas khusus ke kode Anda, yang terlihat sangat umum (selain panggilan, tentunya
yield
). Anda hanya perlu membuat pembungkus yang mengiterasi generator dengan cara khusus. Dengan demikian, pembungkus menambahkan fungsionalitas yang diperlukan ke generator, yang bisa berupa apa saja! Generator memiliki kemungkinan yang hampir tidak terbatas, ini semua tentang imajinasi kita.