Alur kontrol adalah urutan penafsir JavaScript mengeksekusi pernyataan. Jika tidak menyertakan pernyataan yang mengubah alurnya, skrip akan dijalankan dari awal hingga akhir, satu baris dalam satu waktu. Struktur kontrol digunakan untuk menentukan apakah serangkaian pernyataan dijalankan berdasarkan serangkaian kriteria yang ditentukan, mengeksekusi serangkaian pernyataan berulang kali, atau mengganggu urutan pernyataan.
Pernyataan bersyarat
Pernyataan kondisional menentukan apakah kode harus dieksekusi berdasarkan satu atau
beberapa kondisi. Pernyataan kondisional mengeksekusi kode yang dimuatnya jika
kondisi yang terkait (atau serangkaian kondisi) bernilai true
. Jika tidak, kode akan dilewati.
if
…else
Pernyataan if
mengevaluasi kondisi di dalam tanda kurung yang cocok yang
mengikutinya. Jika kondisi di dalam tanda kurung bernilai true
, pernyataan atau pernyataan blok
yang mengikuti tanda kurung yang cocok akan dieksekusi:
if ( true ) console.log( "True." );
> "True."
if ( true ) {
const myString = "True.";
console.log( myString );
}
> "True."
Jika kondisi di dalam tanda kurung bernilai false
, pernyataan yang
mengikutinya akan diabaikan:
if ( false ) console.log( "True." );
Kata kunci else
tepat setelah pernyataan if
dan
pernyataan yang dieksekusi secara bersyarat menentukan pernyataan yang akan dijalankan jika
kondisi if
bernilai false
:
if ( false ) console.log( "True." )''
else console.log( "False" );
> "False."
Untuk merangkai beberapa pernyataan if
, Anda dapat membuat
pernyataan yang dieksekusi secara bersyarat setelah else
pernyataan if
lainnya:
const myCondition = 2;
if ( myCondition === 5 ) console.log( "Five." );
else if ( myCondition === 2 ) console.log( "Two." );
Sebaiknya gunakan sintaksis pernyataan blok mengikuti kondisional untuk
meningkatkan keterbacaan, tetapi klausa else if
sering kali merupakan pengecualian untuk hal ini:
if ( myCondition === 5 ) {
console.log( "Five." );
} else if ( myCondition === 3 ) {
console.log( "Three" );
} else {
console.log( "Neither five nor three." );
}
> "Neither five nor three."
Operator ternary
if
mengeksekusi pernyataan secara bersyarat. Operator ternary (lebih akurat
tetapi lebih jarang disebut sebagai operator kondisional ternary) adalah singkatan yang digunakan
untuk mengeksekusi ekspresi secara bersyarat. Sesuai dengan namanya, operator ternary adalah satu-satunya operator JavaScript yang menggunakan tiga operand:
- Kondisi yang akan dievaluasi, diikuti dengan tanda tanya (
?
). - Ekspresi yang akan dieksekusi jika kondisi bernilai
true
, diikuti dengan titik dua (:
). - Ekspresi yang akan dieksekusi jika kondisi bernilai
false
.
Parameter ini sering digunakan untuk menetapkan atau meneruskan nilai secara bersyarat:
const myFirstResult = true ? "First value." : "Second value.";
const mySecondResult = false ? "First value." : "Second value.";
myFirstResult;
> "First value."
mySecondResult;
> "Second value."
switch
…case
Gunakan pernyataan switch
untuk membandingkan nilai ekspresi dengan daftar nilai potensial yang ditentukan menggunakan satu atau beberapa kata kunci case
. Sintaksis ini
tidak biasa karena berasal dari beberapa keputusan desain paling awal JavaScript.
Sintaksis switch
...case
menggunakan kata kunci switch
, diikuti dengan ekspresi yang akan dievaluasi dan digabungkan dalam tanda kurung, diikuti dengan sepasang tanda kurung kurawal yang cocok.
Isi switch
dapat berisi kata kunci case
, biasanya satu atau beberapa,
diikuti dengan ekspresi atau nilai, diikuti dengan titik dua (:
).
Saat mencapai case
dengan nilai yang cocok dengan ekspresi yang
dievaluasi dalam tanda kurung setelah kata kunci switch
, penafsir akan mengeksekusi
pernyataan apa pun yang mengikuti klausa case
tersebut:
switch ( 2 + 2 === 4 ) {
case false:
console.log( "False." );
case true:
console.log( "True." );
}
> "True."
Semua pernyataan setelah case
yang cocok akan dieksekusi, meskipun
disertakan dalam pernyataan blok.
switch ( 2 + 2 === 4 ) {
case false:
console.log( "False." );
case true:
let myVariable = "True.";
console.log( myVariable );
}
> "True."
Salah satu kesalahan menggunakan switch…case
adalah, setelah kecocokan ditemukan, penafsir JavaScript akan mengeksekusi apa pun pernyataan yang mengikuti case
yang cocok, bahkan pernyataan dalam klausa case
lainnya. Hal ini disebut "berpindah" ke
case
berikutnya:
switch ( 2 + 2 === 7 ) {
case false:
console.log( "False." );
case true:
console.log( "True." );
}
> "False."
> "True."
Untuk mencegah kegagalan, akhiri setiap kasus dengan kata kunci break
, yang
akan segera menghentikan evaluasi isi switch
:
switch ( 2 + 2 === 7 ) {
case false:
console.log( "False." );
break;
case true:
console.log( "True." );
break;
}
> "False."
Jika tidak ada case
yang cocok dengan nilai kondisional, switch
akan memilih klausa default
jika ada:
switch ( 20 ) {
case 5:
console.log( "The value was five." );
break;
case 10:
console.log( "The value was ten." );
break;
default:
console.log( "The value was something unexpected." );
}
> "The value was something unexpected."
Namun, transisi juga berlaku untuk default
, sehingga berpotensi menyebabkan
hasil yang tidak terduga. Untuk memperbaikinya, akhiri pernyataan default
Anda dengan break
, atau tempatkan di akhir daftar kasus.
switch ( 20 ) {
default:
console.log( "The value was something unexpected." );
case 10:
console.log( "The value was ten." );
break;
case 5:
console.log( "The value was five." );
break;
}
> The value was something unexpected.
> The value was ten.
Karena klausa case
tidak memerlukan
pernyataan blok untuk mengelompokkan
beberapa pernyataan, klausa case
dan default
tidak membuat
cakupan leksikal dengan sendirinya:
let myVariable;
switch ( true ) {
case true:
let myVariable = "True.";
break;
default:
let myVariable = "False.";
break;
}
> Uncaught SyntaxError: redeclaration of let myVariable
Untuk mengelola cakupan, gunakan pernyataan pemblokiran:
let myVariable;
switch ( true ) {
case true: {
let myVariable = "True.";
break;
}
default: {
let myVariable = "False.";
break;
}
}
Loop dan iterasi
Loop memungkinkan Anda mengulangi serangkaian pernyataan selama suatu kondisi terpenuhi, atau hingga suatu kondisi terpenuhi. Gunakan loop untuk mengeksekusi serangkaian petunjuk dengan frekuensi tetap, hingga hasil tertentu tercapai, atau sampai penafsir mencapai akhir struktur data yang dapat dilakukan (misalnya, elemen akhir dalam array, peta, atau kumpulan, properti akhir suatu objek, atau karakter terakhir dalam string).
Loop mengganggu alur "atas ke bawah" dalam eksekusi skrip dengan melakukan iterasi pada serangkaian pernyataan hingga satu atau beberapa kondisi terpenuhi, atau tidak lagi terpenuhi, bergantung pada sintaksis yang digunakan untuk membuat loop. Setelah loop berakhir, eksekusi akan melanjutkan ke pernyataan yang mengikutinya. Pada contoh berikut, pernyataan dalam isi loop dijalankan tiga kali sebelum penerjemah melanjutkan:
let iterationCount = 0;
console.log( "Pre-loop." );
while( iterationCount < 3 ) {
iterationCount++;
console.log( "Loop iteration." );
}
console.log( "Continuing on." );
> "Pre-loop."
> "Loop iteration."
> "Loop iteration."
> "Loop iteration."
> "Continuing on."
Jika kondisi tidak dapat dipenuhi selama eksekusi loop, loop akan berlanjut tanpa batas. Loop tanpa batas adalah kesalahan pemrograman umum yang dapat menyebabkan thread eksekusi utama dijeda tanpa batas waktu, atau bahkan membuat error pada tab browser.
Contoh berikut dijalankan selama nilai boolean true
tetap
true
. Karena nilai boolean tidak dapat diubah, hal ini akan menghasilkan loop tak terbatas.
console.log( "Pre-loop." );
while( true ) {
console.log( "Loop iteration." );
}
> "Pre-loop."
> "Loop iteration."
> "Loop iteration."
> "Loop iteration."
> "Loop iteration."
> "Loop iteration."
…
Hindari meninggalkan loop tanpa batas dalam kode produksi. Jika tidak sengaja membuatnya selama pengembangan, Anda dapat memperbaikinya dengan menutup tab browser tempatnya berjalan, mengupdate kode agar loop tidak lagi tak terbatas, dan membuka kembali halaman tersebut.
while
Loop while
dibuat menggunakan kata kunci while
, diikuti dengan sepasang tanda kurung yang cocok yang berisi kondisi yang akan dievaluasi. Jika kondisi yang ditentukan
awalnya bernilai true
, pernyataan (atau
pernyataan blok) yang mengikuti
tanda kurung tersebut akan dieksekusi. Jika tidak, loop tidak akan pernah berjalan. Setelah setiap iterasi, kondisinya dievaluasi ulang, dan jika masih true
, loop akan diulang.
let iterationCount = 0;
while( iterationCount < 3 ) {
iterationCount++;
console.log( `Loop ${ iterationCount }.` );
}
> "Loop 1."
> "Loop 2."
Jika menemukan pernyataan continue
dalam loop while
, penafsir akan menghentikan iterasi tersebut, mengevaluasi ulang kondisinya, dan melanjutkan loop jika memungkinkan:
let iterationCount = 0;
while( iterationCount <= 5 ) {
iterationCount++;
if( iterationCount === 3 ) {
continue;
}
console.log( `Loop ${ iterationCount }.` );
}
console.log( "Loop ended." );
> "Loop 1."
> "Loop 2."
> "Loop 4."
> "Loop 5."
> "Loop ended."
Jika penafsir menemukan pernyataan break
dalam loop while
, iterasi tersebut
akan dihentikan dan kondisinya tidak dievaluasi ulang, sehingga memungkinkan penafsir melanjutkan:
let iterationCount = 1;
while( iterationCount <= 5 ) {
if( iterationCount === 3 ) {
console.log(`Iteration skipped.``);`
break;
}
console.log( `Loop ${ iterationCount }.` );
iterationCount++;
}
console.log( "Loop ended." );
> "Loop 1."
> "Loop 2."
> "Iteration skipped.
> "Loop ended."
Anda dapat menggunakan while
untuk melakukan iterasi sebanyak mungkin, seperti yang terlihat pada
contoh sebelumnya, tetapi kasus penggunaan yang paling umum untuk while
adalah loop dengan
panjang tidak pasti:
let randomize = () => Math.floor( Math.random() * 10 );
let randomNum = randomize();
while( randomNum !== 3 ){
console.log( `The number is not ${ randomNum }.` );
randomNum = randomize();
}
console.log( `The correct number, ${ randomNum }, was found.` );
> "The number is not 0."
> "The number is not 6."
> "The number is not 1."
> "The number is not 8."
> "The correct number, 3, was found."
do
…while
do
...while
adalah varian loop while
yang memungkinkan evaluasi bersyarat terjadi di akhir setiap iterasi loop. Artinya, isi loop selalu dijalankan setidaknya sekali.
Untuk membuat loop do
...while
, gunakan kata kunci do
yang diikuti dengan pernyataan (atau pernyataan blok) untuk dieksekusi pada setiap iterasi loop. Segera setelah pernyataan tersebut, tambahkan
while
dan cocokkan tanda kurung yang berisi kondisi yang akan dievaluasi. Jika kondisi ini tidak lagi bernilai true
, loop akan berakhir.
let iterationCount = 1;
do {
console.log( `Loop ${ iterationCount }.` );
iterationCount++;
} while ( iterationCount < 3 );
> "Loop 1."
> "Loop 2."
> "Loop 3."
Seperti loop while
, kasus penggunaan yang paling umum untuk do
...while
adalah loop dengan panjang yang tidak tentu:
let randomNum;
do {
randomNum = ( () => Math.floor( Math.random() * 10 ) )();
console.log( `Is the number ${ randomNum }?` );
} while ( randomNum !== 3 );
console.log( `Yes, ${ randomNum } was the correct number.` );
> "Is the number 9?"
> "Is the number 2?"
> "Is the number 8?"
> "Is the number 2?"
> "Is the number 3?"
> "Yes, 3 was the correct number."
for
Gunakan loop for
untuk melakukan iterasi pada jumlah yang diketahui. Pada codebase lama, hal ini
sering digunakan untuk melakukan iterasi pada elemen dalam array.
Untuk membuat loop for
, gunakan kata kunci for
, diikuti dengan sepasang tanda kurung
yang menerima tiga ekspresi berikut secara berurutan dan dipisahkan dengan
titik koma:
- Ekspresi yang akan dievaluasi saat loop dimulai
- Kondisi yang menentukan apakah loop harus dilanjutkan
- Ekspresi yang akan dieksekusi di akhir setiap loop
Setelah tanda kurung ini, tambahkan pernyataan (biasanya pernyataan blok) yang akan dijalankan selama loop.
for( let i = 0; i < 3; i++ ) {
console.log( "This loop will run three times.")
}
Ekspresi pertama menginisialisasi variabel yang bertindak sebagai penghitung. Ekspresi ini dievaluasi sekali, sebelum iterasi pertama loop. Anda dapat menginisialisasi variabel ini menggunakan let
(atau var
, secara historis) seperti variabel lainnya, dan cakupannya adalah isi loop. Variabel ini dapat memiliki ID yang valid, tetapi sering disebut i
untuk "iterasi" atau "indeks".
Hal ini tampaknya bertentangan dengan
praktik terbaik untuk nama ID yang dapat diprediksi,
tetapi konvensi ini cukup dibuat untuk jelas bagi developer lain
secara sekilas. Karena koleksi yang diindeks tidak diindeks, variabel ini hampir selalu memiliki nilai awal 0
.
Seperti bentuk loop lainnya, kondisinya adalah ekspresi yang menentukan apakah loop harus dijalankan atau tidak. Ini paling sering digunakan untuk menetapkan batas
atas untuk penghitung iterasi. Penafsir mengevaluasi kondisi sebelum
menjalankan loop for
untuk pertama kalinya.Jika kondisi pada awalnya tidak
bernilai true
, isi loop tidak akan dijalankan.
Ekspresi akhir dieksekusi di akhir setiap iterasi melalui loop. Ini biasanya digunakan untuk menambah ID satu per satu.
Anda paling sering melihat loop for
yang melakukan iterasi melalui array dalam codebase lama. Dalam kasus ini, kondisi yang ditetapkan untuk melanjutkan loop adalah jumlah iterasi yang kurang dari atau sama dengan panjang array yang sedang diiterasi. Variabel yang digunakan untuk melacak jumlah iterasi saat ini digunakan untuk mencari
nilai yang terkait dengan indeks tersebut dalam array, yang memungkinkan setiap elemen dalam
array dapat ditindaklanjuti secara berurutan:
var myArray = [ true, false, true ];
for( let i = 0; i <= myArray.length; i++ ) {
console.log( myArray[ i ] );
}
> true
> false
> true
Pendekatan ini tidak lagi digunakan dan digantikan dengan pendekatan yang lebih modern untuk loop melalui struktur data iterable.
for
[...] of
[...]
Gunakan loop for
...of
... untuk melakukan iterasi pada nilai yang tersimpan dalam struktur data yang dapat diiterasi, seperti array, set, atau peta.
Loop for
...of
... menggunakan kata kunci for
, diikuti sepasang tanda kurung
yang berisi variabel, diikuti dengan of
, lalu struktur data yang sedang diiterasi. Variabel dapat berupa deklarasi yang dilakukan di sini menggunakan let
, const
, atau
var
, variabel yang dideklarasikan sebelumnya dalam cakupan saat ini, properti
objek, atau instance
penetapan destrukturisasi.
File ini berisi nilai elemen yang sesuai dengan iterasi loop saat ini.
const myIterable = [ true, false, true ];
for( const myElement of myIterable ) {
console.log( myElement );
}
> true
> false
> true
Dalam contoh ini, penggunaan const
untuk myElement
akan berfungsi meskipun myElement
diberi nilai baru di setiap iterasi loop. Hal ini karena variabel
yang dideklarasikan dengan let
atau const
dicakup dalam pernyataan blok dalam
loop. Variabel diinisialisasi pada awal setiap iterasi, dan dihapus pada akhir iterasi.
for
…in
…
Gunakan loop for
...in
... untuk melakukan iterasi pada properti yang dapat dihitung dari suatu objek, termasuk properti turunan yang dapat dihitung. Seperti pada loop for
...of
..., loop for
...in
... menggunakan kata kunci for
, diikuti sepasang tanda kurung berisi variabel yang berisi nilai kunci properti yang sesuai dengan iterasi loop saat ini. Variabel ini diikuti oleh kata kunci in
, lalu objek yang diiterasi:
const myObject = { "myProperty" : true, "mySecondProperty" : false };
for( const myKey in myObject ) {
console.log( myKey );
}
> "myProperty"
> "mySecondProperty"
Sekali lagi, meskipun nilai myKey
berubah di setiap iterasi loop, Anda dapat menggunakan const
tanpa error karena variabel dihapus secara efektif di akhir setiap iterasi, lalu dibuat ulang di awal.
Nilai yang terkait dengan setiap kunci properti tidak secara langsung tersedia untuk sintaksis for
...in
.... Namun, karena loop memiliki akses ke kunci properti di setiap iterasi, Anda dapat menggunakan kunci tersebut untuk "mencari" nilainya:
const myObject = { "myProperty" : true, "mySecondProperty" : false };
for( const myKey in myObject ) {
const myValue = myObject[ myKey ];
console.log( `${ myKey } : ${ myValue }` );
}
> "myProperty : true"
> "mySecondProperty : false"
Properti yang diwarisi dari konstruktor bawaan tidak dapat dihitung, yang berarti bahwa
for
...in
... tidak melakukan iterasi melalui properti yang diwarisi dari konstruktor Object
. Namun, semua properti yang dapat dihitung dalam
rantai prototipe objek akan disertakan:
const myPrototype = { "protoProperty" : true };
const myObject = Object.create( myPrototype, {
myProperty: {
value: true,
enumerable: true
}
});
for ( const myKey in myObject ) {
const myValue = myObject[ myKey ];
console.log( `${ myKey } : ${ myValue }` );
}
> "myProperty : true"
> "protoProperty : true"
JavaScript menyediakan metode bawaan untuk menentukan apakah properti merupakan
properti langsung dari objek, bukan properti pada rantai prototipe
objek: metode Object.hasOwn()
modern
dan Object.prototype.hasOwnProperty()
lama. Metode
ini mengevaluasi apakah properti tertentu diwarisi (atau tidak dideklarasikan),
yang menampilkan true
hanya untuk properti langsung objek tertentu:
const myPrototype = { "protoProperty" : true };
const myObject = Object.create( myPrototype, {
myProperty: {
value: true,
enumerable: true
}
});
for ( const myKey in myObject ) {
const myValue = myObject[ myKey ];
if ( Object.hasOwn( myObject, myKey ) ) {
console.log( `${ myKey } : ${ myValue }` );
}
}
> "myProperty : true"
Ada juga tiga metode statis yang masing-masing menampilkan Array yang terdiri dari kunci yang dapat dihitung milik Objek (Object.keys()
), nilai (Object.values()
), atau pasangan nilai kunci (Object.entries()
):
const myObject = { "myProperty" : true, "mySecondProperty" : false };
Object.keys( myObject );
> Array [ "myProperty", "mySecondProperty" ]
Hal ini memungkinkan Anda melakukan iterasi kunci, nilai, atau key-value pair Objek (menggunakan penetapan destrukturisasi) tanpa menyertakan properti yang dimiliki oleh prototipe Objek tersebut:
const myPrototype = { "protoProperty" : "Non-enumerable property value." };
const myObject = Object.create( myPrototype, {
myProperty: {
value: "Enumerable property value.",
enumerable: true
}
});
for ( const propKey of Object.keys( myObject ) ) {
console.log( propKey );
}
> "myProperty"
for ( const propValue of Object.values( myObject ) ) {
console.log( propValue );
}
> "Enumerable property value."
for ( const [ propKey, propValue ] of Object.entries( myObject ) ) {
console.log( `${ propKey } : ${ propValue }` );
}
> "myProperty : Enumerable property value."
forEach()
Metode forEach()
yang disediakan oleh konstruktor Array, Map, Set, dan NodeList memberikan cara pintas yang berguna untuk melakukan iterasi melalui struktur data dalam konteks fungsi callback. Tidak seperti bentuk loop lainnya, loop yang dibuat dengan metode forEach()
apa pun tidak dapat diganggu menggunakan break
atau continue
.
forEach
adalah metode yang dimiliki oleh setiap prototipe struktur data. Setiap metode forEach
mengharapkan fungsi callback sebagai argumen, meskipun metode ini sedikit berbeda dalam hal argumen yang disertakan saat fungsi tersebut dipanggil. Argumen opsional kedua menentukan nilai this
yang akan digunakan sebagai konteks pemanggil untuk fungsi callback.
Fungsi callback yang digunakan dengan Array.forEach
menyediakan parameter yang berisi nilai elemen saat ini, indeks elemen saat ini, dan array tempat metode forEach
dipanggil:
const myArray = [ true, false ];
myArray.forEach( ( myElement, i, originalArray ) => {
console.log( i, myElement, originalArray );
});
> 0 true Array(3) [ true, false ]
> 1 false Array(3) [ true, false ]
Fungsi callback yang digunakan dengan Map.forEach
menyediakan parameter yang berisi nilai yang terkait dengan elemen saat ini, kunci yang terkait dengan elemen saat ini, dan Peta tempat metode forEach
dipanggil:
const myMap = new Map([
['myKey', true],
['mySecondKey', false ],
]);
myMap.forEach( ( myValue, myKey, originalMap ) => {
console.log( myValue, myKey, originalMap );
});
> true "myKey" Map { myKey → true, mySecondKey → false }
> false "mySecondKey" Map { myKey → true, mySecondKey → false }
Callback Set.forEach
menyertakan parameter serupa. Karena Set tidak memiliki indeks atau kunci yang berbeda dari nilai, argumen kedua memberikan nilai yang berlebihan dan dapat diabaikan, semata-mata untuk menjaga sintaksis konsisten dengan metode forEach
lainnya.
const mySet = new Set([ true, false ]);
mySet.forEach( ( myValue, myKey, originalSet ) => {
console.log( myValue, myKey, originalSet );
});
> true true Set [ true, false ]
> false false Set [ true, false ]
Iterator
Iterable adalah struktur data yang terdiri dari elemen individual yang dapat
diiterasi menggunakan pendekatan yang dijelaskan sebelumnya. iterator adalah objek iterable yang mengikuti protokol iterator, yang berarti ia harus mengimplementasikan metode next()
yang bergerak maju melalui elemen-elemennya satu per satu, setiap kali metode tersebut dipanggil, dan menampilkan objek untuk setiap elemen berurutan dalam format tertentu.
Struktur data iterable bawaan JavaScript (seperti Array, Map, dan Set) bukanlah iterator, tetapi semuanya mewarisi metode iterator
, yang dapat diakses menggunakan @@iterator
Simbol terkenal, yang menampilkan objek iterator yang dibuat dari struktur data iterable:
const myIterable = [ 1, 2, 3 ];
const myIterator = myIterable[ Symbol.iterator ]();
myIterable;
> (3) [1, 2, 3]
myIterator;
> Array Iterator {}
Memanggil metode next()
pada iterator akan menelusuri elemen-elemen yang terdapat di dalamnya satu per satu, dengan setiap panggilan menampilkan objek yang berisi dua properti: value
, yang berisi nilai elemen saat ini, dan done
, boolean yang memberi tahu apakah iterator telah meneruskan elemen terakhir dalam struktur data. Nilai done
hanya true
jika panggilan ke next()
menghasilkan upaya untuk mengakses elemen di luar elemen terakhir dalam iterator.
const myIterable = [ 1, 2, 3 ];
const myIterator = myIterable[ Symbol.iterator ]();
myIterator.next();
> Object { value: 1, done: false }
myIterator.next();
> Object { value: 2, done: false }
myIterator.next();
> Object { value: 3, done: false }
myIterator.next();
> Object { value: undefined, done: true }
Fungsi Generator
Gunakan kata kunci function*
(perhatikan tanda bintang) untuk mendeklarasikan fungsi
generator atau menentukan ekspresi fungsi generator:
function* myGeneratorFunction() { };
Seperti iterator, fungsi generator mempertahankan status. Memanggil fungsi generator akan menampilkan objek Generator baru, tetapi tidak langsung mengeksekusi kode dalam isi fungsi:
function* myGeneratorFunction() {
console.log( "Generator function body ")
};
const myGeneratorObject = myGeneratorFunction();
myGeneratorObject;
> Generator { }
typeof myGeneratorObject;
> "object"
Objek generator mengikuti protokol iterator. Nilai yang ditampilkan setiap panggilan ke
next()
pada fungsi generator ditentukan oleh ekspresi yield
,
yang menjeda eksekusi fungsi generator dan menampilkan nilai
ekspresi yang berisi kata kunci yield
. Panggilan berikutnya ke next()
akan melanjutkan eksekusi fungsi, yang akan dijeda pada ekspresi yield
berikutnya dan menampilkan nilai terkait.
function* myGeneratorFunction() {
yield "My first yielded value.";
yield "My second yielded value.";
};
const myGeneratorObject = myGeneratorFunction();
myGeneratorObject.next();
> Object { value: "My first yielded value.", done: false }
myGeneratorObject.next();
> Object { value: "My second yielded value.", done: false }
Saat next()
dipanggil setelah tidak ada nilai lebih lanjut yang ditentukan menggunakan yield
, return
, atau throw
(jika terjadi error), sisa isi fungsi akan dieksekusi, dan Objek yang ditampilkan memiliki value
dari undefined
dan properti done
dari true
:
function* myGeneratorFunction() {
console.log( "Start of the generator function." );
yield "First";
console.log( "Second part of the generator function." );
yield "Second";
console.log( "Third part of the generator function." );
yield "Third";
};
const myGeneratorObject = myGeneratorFunction();
myGeneratorObject.next();
> "Start of the generator function."
> Object { value: "First", done: false }
myGeneratorObject.next();
> "Second part of the generator function."
> Object { value: "Second", done: false }
myGeneratorObject.next();
> "Third part of the generator function."
> Object { value: "Third", done: false }
myGeneratorObject.next();
> Object { value: undefined, done: true }
Gunakan next()
hanya pada Objek yang ditampilkan oleh fungsi generator, bukan fungsi generator itu sendiri. Jika tidak, setiap panggilan ke fungsi generator akan membuat Object generator baru:
function* myGeneratorFunction() {
yield "First";
yield "Second";
};
myGeneratorFunction().next();
> Object { value: "First", done: false }
myGeneratorFunction().next();
> Object { value: "First", done: false }
Seperti fungsi lainnya, fungsi generator akan berhenti saat menemukan kata kunci
return
. Objek ini kemudian menampilkan Objek ke konteks pemanggilan yang berisi
nilai yang ditampilkan dan properti done
dengan nilai true
.
function* myGeneratorFunction() {
yield 1;
yield 2;
return 3;
};
const myGeneratorObject = myGeneratorFunction();
myGeneratorObject.next().done;
> Object { value: 1, done: false }
myGeneratorObject.next().done;
> Object { value: 2, done: false }
myGeneratorObject.next();
> Object { value: 3, done: true }
Ekspresi yield
dapat mengambil beberapa semantik ID,
sehingga memungkinkan "komunikasi" dua arah dari dan kembali ke bagian yang ditangguhkan dari
fungsi generator. Saat nilai diteruskan ke metode next()
generator sebagai
argumen, nilai tersebut akan menggantikan nilai yang terkait dengan ekspresi yield
yang ditangguhkan sebelumnya:
function* myGeneratorFunction() {
const firstYield = yield;
yield firstYield + 10;
};
const myGeneratorObject = myGeneratorFunction();
myGeneratorObject.next();
> Object { value: undefined, done: false }
myGeneratorObject.next( 5 );
> Object { value: 15, done: false }
Perlu diingat bahwa ini menggantikan seluruh ekspresi yang terkait dengan
yield
sebelumnya, dan tidak hanya menetapkan ulang nilai yield
sebelumnya ke
nilai yang ditentukan di next()
:
function* myGeneratorFunction() {
const firstYield = yield;
const secondYield = yield firstYield + 100;
yield secondYield + 10;
};
const myGeneratorObject = myGeneratorFunction();
myGeneratorObject.next();
> Object { value: undefined, done: false }
myGeneratorObject.next( 10 ); // Can be thought of as changing the value of the `firstYield` variable to `10
> Object { value: 110, done: false }
myGeneratorObject.next( 20 ); // Can be thought of as changing the value of the `secondYield` variable to `20`, _not_ `20 + 100;`
> Object { value: 30, done: false }
Argumen apa pun yang diteruskan ke panggilan pertama ke next()
akan diabaikan, karena tidak ada
ekspresi yield
sebelumnya yang akan menerima nilai tersebut. Seperti fungsi lainnya, argumen yang diteruskan ke panggilan fungsi generator awal tersedia di seluruh cakupan isi fungsi generator:
function* myGeneratorFunction( startingValue ) {
let newValue = yield startingValue + 1;
newValue = yield newValue + 10;
yield startingValue + 20;
};
const myGeneratorObject = myGeneratorFunction( 2 );
myGeneratorObject.next( 1 );
> Object { value: 3, done: false }
myGeneratorObject.next( 5 );
> Object { value: 15, done: false }
myGeneratorObject.next( 10 );
Object { value: 22, done: false }
Operator yield*
(perhatikan tanda bintang) digunakan dengan iterable, seperti
fungsi generator lainnya, untuk melakukan iterasi dan menghasilkan setiap nilai yang ditampilkan oleh
operannya:
function* mySecondaryGenerator() {
yield 2;
yield 3;
}
function* myGenerator() {
yield 1;
yield* mySecondaryGenerator();
yield 4;
return 5;
}
const myIterator = myGenerator();
myIterator.next();
> Object { value: 1, done: false }
myIterator.next();
> Object { value: 2, done: false }
myIterator.next();
> Object { value: 3, done: false }
myIterator.next();
> Object { value: 4, done: false }
myIterator.next();
> Object { value: 5, done: true }
JavaScript Asinkron
Meskipun JavaScript pada dasarnya sinkron dalam eksekusi, ada mekanisme yang memungkinkan developer memanfaatkan loop peristiwa untuk melakukan tugas asinkron.
Promise
Promise adalah placeholder untuk nilai yang tidak diketahui saat promise dibuat. Ini adalah container yang menentukan operasi asinkron, istilah yang digunakan untuk operasi dianggap berhasil atau gagal, tindakan yang harus diambil dalam kedua kasus tersebut, dan nilai yang dihasilkan.
Buat instance Promise menggunakan operator new
dengan fungsi konstruktor Promise
bawaan. Konstruktor ini menerima fungsi yang disebut executor
sebagai argumen. Fungsi eksekutor tersebut biasanya digunakan untuk melakukan satu atau beberapa
tindakan asinkron, lalu mendikte persyaratan yang akan membuat Promise dianggap
berhasil dipenuhi atau ditolak. Promise didefinisikan sebagai tertunda
saat fungsi eksekutor berjalan. Setelah eksekutor selesai, Promise
dianggap terpenuhi (atau diselesaikan, dalam beberapa sumber dokumentasi) jika
fungsi eksekutor dan tindakan asinkron yang dijalankannya berhasil
diselesaikan, dan ditolak jika fungsi eksekutor mengalami error, atau
tindakan asinkron yang dilakukan gagal. Setelah dipenuhi atau ditolak, Promise akan dianggap selesai.
const myPromise = new Promise( () => { });
Konstruktor memanggil fungsi eksekutor dengan dua argumen. Argumen tersebut adalah fungsi yang memungkinkan Anda memenuhi atau menolak Promise secara manual:
const myPromise = new Promise( ( fulfill, reject ) => { });
Fungsi yang digunakan untuk memenuhi atau menolak Promise dipanggil dengan nilai Promise yang dihasilkan sebagai argumen (biasanya error untuk penolakan):
const myPromise = new Promise( ( fulfill, reject ) => {
const myResult = true;
setTimeout(() => {
if( myResult === true ) {
fulfill( "This Promise was successful." );
} else {
reject( new Error( "This Promise has been rejected." ) );
}
}, 10000);
});
myPromise;
> Promise { <state>: "pending" }
myPromise;
> Promise { <state>: "fulfilled", <value>: "This Promise was successful." }
Perantaian Promise
Objek Promise yang dihasilkan dapat ditindaklanjuti menggunakan metode then()
, catch()
, dan finally()
yang diwarisi dari konstruktor Promise. Setiap metode ini menampilkan Promise, yang dapat langsung ditindaklanjuti dengan then()
, catch()
, atau finally()
lagi, sehingga Anda dapat merangkai Promise yang dihasilkan.
then()
menyediakan dua fungsi callback sebagai argumen. Gunakan fungsi pertama untuk memenuhi Promise yang dihasilkan, dan fungsi kedua untuk menolaknya. Kedua metode tersebut menerima satu
argumen yang memberikan nilai pada Promise yang dihasilkan.
const myPromise = new Promise( ( fulfill, reject ) => {
const myResult = true;
setTimeout(() => {
if( myResult === true ) {
fulfill( "This Promise was fulfilled." );
} else {
reject( new Error( "This Promise has been rejected." ) );
}
}, 100);
});
myPromise.then( successfulResult => console.log( successfulResult ), failedResult => console.error( failedResult ) );
> "This Promise was successful."
Anda juga dapat menggunakan then()
untuk hanya menangani status yang terpenuhi, dan catch
untuk
menangani status ditolak. Panggil catch
dengan satu argumen yang berisi nilai yang diberikan dalam metode penolakan Promise:
const myPromise = new Promise( ( fulfill, reject ) => {
const myResult = false;
setTimeout(() => {
if( myResult === true ) {
fulfill( "This Promise was fulfilled." );
} else {
reject( new Error( "This Promise has been rejected." ) );
}
}, 100);
});
myPromise
.then( fulfilledResult => console.log(fulfilledResult ) )
.catch( rejectedResult => console.log( rejectedResult ) )
.finally( () => console.log( "The Promise has settled." ) );
> "Error: This Promise has been rejected."
> "The Promise has settled."
Tidak seperti then
dan catch
yang memungkinkan fungsi pengendali berjalan saat Promise terpenuhi atau ditolak, fungsi yang diteruskan sebagai argumen ke metode finally
akan dipanggil, terlepas dari apakah Promise terpenuhi atau ditolak.
Fungsi pengendali dipanggil tanpa argumen, karena tidak dimaksudkan untuk digunakan dengan nilai yang diteruskan dari Promise, tetapi hanya untuk mengeksekusi kode setelah Promise selesai.
Serentak
Konstruktor Promise menyediakan empat metode untuk menangani beberapa Promise terkait, menggunakan iterable yang berisi objek Promise. Masing-masing metode ini menampilkan Promise, yang terpenuhi atau ditolak berdasarkan status Promise yang diteruskan padanya. Misalnya, Promise.all()
membuat Promise yang terpenuhi hanya jika setiap Promise yang diteruskan ke metode tersebut terpenuhi:
const firstPromise = new Promise( ( fulfill, reject ) => fulfill( "Successful. ") );
const secondPromise = new Promise( ( fulfill, reject ) => fulfill( "Successful. ") );
const thirdPromise = new Promise( ( fulfill, reject ) => fulfill( "Successful. ") );
const failedPromise = new Promise( ( fulfill, reject ) => reject( "Failed.") );
const successfulPromises = [ firstPromise, secondPromise, thirdPromise ];
const oneFailedPromise = [ failedPromise, ...successfulPromises ];
Promise.all( successfulPromises )
.then( ( allValues ) => {
console.log( allValues );
})
.catch( ( failValue ) => {
console.error( failValue );
});
> Array(3) [ "Successful. ", "Successful. ", "Successful. " ]
Promise.all( oneFailedPromise )
.then( ( allValues ) => {
console.log( allValues );
})
.catch( ( failValue ) => {
console.error( failValue );
});
> "Failed."
Metode serentak Promise adalah sebagai berikut:
Promise.all()
- Terpenuhi hanya jika semua Promise yang disediakan terpenuhi.
Promise.any()
- Terpenuhi jika salah satu Promise yang disediakan terpenuhi, dan ditolak hanya jika semua Promise ditolak.
Promise.allSettled()
- Terpenuhi ketika Promise telah selesai, apa pun hasilnya.
Promise.race()
- Ditolak atau dipenuhi berdasarkan hasil Promise pertama yang harus diselesaikan, mengabaikan semua Promise yang ditetapkan kemudian.
async
/await
Jika Anda menggunakan kata kunci async
sebelum deklarasi fungsi atau ekspresi fungsi, setiap nilai yang ditampilkan oleh fungsi akan ditampilkan sebagai Promise yang terpenuhi yang berisi nilai tersebut. Hal ini memungkinkan Anda menjalankan dan mengelola operasi asinkron menggunakan alur kerja
yang sama seperti pengembangan sinkron.
async function myFunction() {
return "This is my returned value.";
}
myFunction().then( myReturnedValue => console.log( myReturnedValue ) );
> "This is my returned value."
Ekspresi await
menjeda eksekusi fungsi asinkron saat Promise terkait diselesaikan. Setelah Promise diselesaikan, nilai ekspresi await
adalah nilai Promise yang terpenuhi atau ditolak.
async function myFunction() {
const myPromise = new Promise( ( fulfill, reject ) => { setTimeout( () => fulfill( "Successful. "), 5000 ); });
const myPromisedResult = await myPromise;
return myPromisedResult;
}
myFunction()
.then( myResult => console.log( myResult ) )
.catch( myFailedResult => console.error( myFailedResult ) );
> "Successful."
Setiap nilai non-Promise yang disertakan dalam ekspresi await
akan ditampilkan sebagai Promise yang telah terpenuhi:
async function myFunction() {
const myPromisedResult = await "String value.";
return myPromisedResult;
}
myFunction()
.then( myResult => console.log( myResult ) )
.catch( myFailedResult => console.error( myFailedResult ) );
> "String value."
Menguji pemahaman Anda
Jenis loop apa yang Anda gunakan untuk melakukan iterasi pada kuantitas yang diketahui?
for
while
do...while