Alur kontrol

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.

ifelse

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."

switchcase

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."

dowhile

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:

  1. Ekspresi yang akan dievaluasi saat loop dimulai
  2. Kondisi yang menentukan apakah loop harus dilanjutkan
  3. 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.

forin

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