Alur kontrol

Alur kontrol adalah urutan eksekusi penafsir JavaScript pernyataan pribadi Anda. Jika skrip tidak menyertakan pernyataan yang mengubah alurnya, itu adalah dieksekusi dari awal hingga akhir, satu baris pada satu waktu. Struktur kontrol yang digunakan untuk menentukan apakah satu set pernyataan dieksekusi atau tidak berdasarkan serangkaian kriteria yang ditentukan, mengeksekusi serangkaian pernyataan berulang kali, atau urutan pernyataan.

Pernyataan bersyarat

Pernyataan bersyarat menentukan apakah kode harus dieksekusi berdasarkan satu atau beberapa kondisi lainnya. Pernyataan kondisional mengeksekusi kode yang dikandungnya jika kondisi terkait (atau kumpulan kondisi) bernilai true. Jika tidak, kode dilewati.

ifelse

Pernyataan if mengevaluasi kondisi di dalam tanda kurung yang cocok yang mengikuti. Jika kondisi di dalam tanda kurung bernilai true, atribut atau pernyataan blokir yang mengikuti tanda kurung yang cocok 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 yang tepat setelah pernyataan if dan kata kunci yang dieksekusi bersyarat menentukan pernyataan yang akan dieksekusi 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 dijalankan bersyarat setelah else pernyataan if lainnya:

const myCondition = 2;
if ( myCondition === 5 ) console.log( "Five." );
else if ( myCondition === 2 ) console.log( "Two." );

Kami sangat menyarankan penggunaan sintaks{i> block statement<i} dengan 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 kurang umum disebut operator bersyarat terner) adalah singkatan yang digunakan untuk mengeksekusi ekspresi secara bersyarat. Sesuai dengan namanya, ternary operator 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.

Hal 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. Sintaks ini adalah tidak biasa karena berasal dari beberapa keputusan desain JavaScript yang paling awal. Sintaksis switch...case menggunakan kata kunci switch, diikuti dengan ekspresi untuk dievaluasi dan digabungkan dalam tanda kurung, diikuti oleh sepasang tanda kurung kurawal yang cocok. Isi switch dapat berisi case kata kunci, biasanya satu atau beberapa, diikuti dengan ekspresi atau nilai, diikuti dengan titik dua (:).

Saat penafsir mencapai case dengan nilai yang cocok dengan ekspresi yang dievaluasi dalam tanda kurung setelah kata kunci switch, model ini akan mengeksekusi pernyataan 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 dijalankan, meskipun jika yang 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 penggunaan switch…case adalah, setelah kecocokan ditemukan, Penerjemah JavaScript mengeksekusi apa pun pernyataan yang mengikuti case yang cocok, bahkan yang ada dalam klausa case lainnya. Hal ini disebut "tumpang-tindih" 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 langsung 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 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, fallback juga berlaku untuk default, yang berpotensi menyebabkan hasil yang tidak diharapkan. Untuk memperbaikinya, akhiri pernyataan default Anda dengan break, atau menempatkannya di urutan terakhir dalam 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 blokir untuk pengelompokan beberapa pernyataan, klausa case dan default tidak dibuat cakupan leksikal mandiri:

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 blok:

let myVariable;
switch ( true ) {
  case true: {
    let myVariable = "True.";
    break;
  }
  default: {
    let myVariable = "False.";
    break;
  }
}

Loop dan iterasi

{i>Loop <i}memungkinkan Anda mengulangi serangkaian pernyataan selama suatu kondisi terpenuhi, atau hingga suatu kondisi terpenuhi. Menggunakan loop untuk menjalankan serangkaian petunjuk yang telah diperbaiki berkali-kali, hingga hasil tertentu tercapai, atau hingga penafsir mencapai akhir dari struktur data iterable (misalnya, elemen akhir dalam array, peta, atau set, properti akhir objek, atau karakter terakhir dalam suatu {i>string<i}).

Pengulangan menginterupsi bagian "atas ke bawah" alur eksekusi skrip dengan melakukan iterasi atas satu set pernyataan hingga satu atau lebih kondisi terpenuhi, atau tidak lagi dipenuhi, tergantung pada sintaks yang digunakan untuk membuat loop. Setelah loop berakhir, eksekusi dilanjutkan ke pernyataan yang mengikutinya. Dalam contoh berikut, pernyataan dalam isi loop dieksekusi tiga kali sebelum penerjemah akan 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 waktu. Loop tak terbatas ini adalah kesalahan pemrograman umum yang dapat menyebabkan thread eksekusi utama untuk menjeda tanpa batas waktu, atau bahkan membuat tab browser error.

Contoh berikut dijalankan selama nilai boolean true tetap ada true. Karena nilai boolean tidak dapat diubah, hal ini menciptakan loop terus-menerus.

console.log( "Pre-loop." );
while( true ) {
console.log( "Loop iteration." );
}
> "Pre-loop."
> "Loop iteration."
> "Loop iteration."
> "Loop iteration."
> "Loop iteration."
> "Loop iteration."
…

Hindari loop tanpa henti dalam kode produksi Anda. Jika Anda secara tidak sengaja membuat satu selama pengembangan, Anda dapat memperbaikinya dengan menutup tab {i>browser<i} yang sedang dijalankan , memperbarui kode Anda sehingga loop tidak lagi tak terbatas, dan membuka kembali kami.

while

Loop while dibuat menggunakan kata kunci while, diikuti dengan sepasang cocok dengan tanda kurung yang berisi kondisi yang akan dievaluasi. Jika yang ditentukan kondisi awalnya bernilai true, pernyataan (atau pernyataan blokir) yang mengikuti tanda kurung itu dieksekusi. Jika tidak, loop tidak pernah berjalan. Setelah setiap iterasi, kondisi akan dievaluasi ulang. Jika masih true, loop berulang.

let iterationCount = 0;
while( iterationCount < 3 ) {
  iterationCount++;
  console.log( `Loop ${ iterationCount }.` );
}
> "Loop 1."
> "Loop 2."

Jika penafsir menemukan pernyataan continue dalam loop while, penafsir akan menghentikannya. iterasi, 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 berhenti dan kondisi tidak dievaluasi ulang, sehingga penerjemah dapat 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 beberapa kali, seperti yang terlihat dalam contoh sebelumnya, tetapi kasus penggunaan yang paling umum untuk while adalah loop panjang tidak tentu:

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 dari loop while yang menggunakan kondisional evaluasi terjadi di akhir setiap iterasi loop. Ini berarti body loop selalu dieksekusi minimal sekali.

Untuk membuat loop do...while, gunakan kata kunci do yang diikuti dengan pernyataan (atau pernyataan blok) yang akan dijalankan pada setiap iterasi loop. Segera setelah pernyataan itu, tambahkan while dan cocok dengan tanda kurung yang berisi kondisi yang akan dievaluasi. Kapan 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 halnya loop while, kasus penggunaan yang paling umum untuk do...while adalah loop dari panjang 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 kuantitas yang diketahui. Dalam codebase lama, ini adalah yang sering digunakan untuk mengulangi elemen dalam sebuah array.

Untuk membuat loop for, gunakan kata kunci for, yang diikuti dengan sepasang tanda kurung yang menerima tiga ekspresi berikut secara berurutan dan dipisahkan oleh titik koma:

  1. Ekspresi yang akan dievaluasi saat loop dimulai
  2. Kondisi yang menentukan apakah loop harus dilanjutkan atau tidak
  3. Ekspresi yang akan dieksekusi pada akhir setiap loop

Setelah tanda kurung ini, tambahkan pernyataan (biasanya pernyataan blokir) menjadi yang dijalankan selama loop.

for( let i = 0; i < 3; i++ ) {
  console.log( "This loop will run three times.")
}

Ekspresi pertama melakukan inisialisasi variabel yang bertindak sebagai penghitung. Ini ekspresi dievaluasi sekali, sebelum iterasi pertama loop. Anda dapat lakukan inisialisasi variabel ini menggunakan let (atau var, secara historis) seperti yang lainnya variabel, dan cakupannya adalah isi dari loop. Variabel-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 mapan untuk jelas bagi pengembang lain di secara sekilas. Karena koleksi yang diindeks tidak diindeks, variabel ini hampir selalu memiliki nilai awal 0.

Seperti halnya bentuk loop lainnya, kondisinya adalah ekspresi yang menentukan apakah loop harus dijalankan. Ini paling sering digunakan untuk menetapkan nilai ke penghitung iterasi. Penafsir mengevaluasi kondisi sebelum mengeksekusi loop for untuk pertama kalinya.Jika kondisi awal tidak dievaluasi ke true, isi loop tidak dijalankan.

Ekspresi akhir dieksekusi pada akhir setiap iterasi melalui loop. Ini biasanya digunakan untuk menambah {i>ID <i}satu per satu.

Anda akan sering melihat loop for melakukan iterasi melalui array di codebase. Dalam kasus ini, kondisi yang ditentukan untuk melanjutkan loop adalah jumlah iterasi kurang dari atau sama dengan panjang array yang diiterasi dilewatkan. Variabel yang digunakan untuk melacak jumlah iterasi ini digunakan untuk melihat nilai yang terkait dengan indeks itu dalam {i>array<i}, memungkinkan setiap elemen dalam himpunan (array) yang akan 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 mendukung pendekatan yang lebih modern untuk melakukan loop melalui struktur data yang dapat dikonversi.

for [...] of [...]

Gunakan loop for...of... untuk melakukan iterasi pada nilai yang disimpan di struktur data iterable, seperti array, set, atau peta.

Loop for...of... menggunakan kata kunci for diikuti dengan sepasang tanda kurung berisi variabel, diikuti dengan of, lalu struktur datanya akan diiterasi berakhir. Variabel dapat berupa deklarasi yang dijalankan di sini menggunakan let, const, atau var, variabel yang dideklarasikan sebelumnya dalam cakupan saat ini, yaitu objek properti, atau instance dari penugasan destrukturisasi. Ini berisi nilai elemen yang sesuai dengan iterasi saat ini dari loop.

const myIterable = [ true, false, true ];
for( const myElement of myIterable ) {
  console.log( myElement );
}
> true
> false
> true

Dalam contoh ini, penggunaan const untuk myElement berfungsi meskipun myElement diberi nilai baru dalam setiap iterasi loop. Hal ini karena variabel yang dideklarasikan dengan let atau const dicakup dalam pernyataan blok dalam . Variabel diinisialisasi pada awal setiap iterasi, dan dihapus pada akhir dari iterasi itu.

for...in...

Gunakan loop for...in... untuk melakukan iterasi pada properti yang dapat dienumerasi dari suatu objek, termasuk properti turunan yang dapat dienumerasi. Seperti halnya loop for...of..., Loop for...in... menggunakan kata kunci for diikuti dengan 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 dengan 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 tersedia langsung untuk for...in.... Namun, karena {i>loop <i}memiliki akses kunci properti pada setiap iterasi, Anda dapat menggunakan kunci itu 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, artinya for...in... tidak melakukan iterasi melalui properti yang diwarisi dari Object . Namun, setiap properti yang bisa dienumerasi dalam atribut rantai prototipe 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 suatu properti merupakan properti langsung objek daripada properti pada prototipe objek jaringan: mesin modern Metode Object.hasOwn() dan Object.prototype.hasOwnProperty() lama. Ini mengevaluasi apakah properti yang ditentukan diwarisi (atau tidak dideklarasikan), menampilkan true hanya untuk properti langsung dari objek yang ditentukan:

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 mengembalikan Array yang terdiri dari Kunci yang dapat dihitung 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 Objek, nilai, atau pasangan nilai kunci (menggunakan penugasan destrukturisasi) tanpa menyertakan properti yang dimiliki oleh prototipe Object 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 Array, Peta, Tetapkan, dan konstruktor NodeList menyediakan cara pintas yang berguna untuk melakukan iterasi pada data dalam konteks fungsi callback. Tidak seperti bentuk loop lainnya, loop yang dibuat dengan metode forEach() apa pun tidak dapat diputus menggunakan break atau continue.

forEach adalah metode yang dimiliki oleh prototipe setiap struktur data. Setiap forEach mengharapkan fungsi callback sebagai argumen, walaupun keduanya sedikit berbeda suku argumen yang disertakan ketika fungsi itu dipanggil. Kedua, opsional 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 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 Map 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 yang serupa. Karena Set tidak memiliki indeks atau kunci yang berbeda dari nilai, argumen kedua justru menyediakan nilai yang redundan dan dapat diabaikan, secara ketat agar {i>syntax<i} tetap 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 apa pun yang terdiri dari elemen individual yang dapat melakukan iterasi menggunakan pendekatan yang telah dijelaskan sebelumnya. iterator adalah objek iterable yang mengikuti protokol iterator, yang berarti ia harus mengimplementasikan metode next() yang maju melalui elemen-elemen yang dikandungnya satu per satu, setiap kali metode tersebut dipanggil, menampilkan objek untuk setiap metode ke dalam format tertentu.

Struktur data iterable bawaan JavaScript (seperti Array, Peta, dan Set) bukan iterator di dan dari sendiri, tetapi mereka semua mewarisi metode iterator, yang dapat diakses menggunakan @@iterator Simbol terkenal, yang mengembalikan 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 melalui elemen-elemen yang berisi satu per satu, di mana setiap panggilan menampilkan objek yang berisi dua properti: value, yang berisi nilai elemen saat ini, dan done, boolean yang memberi tahu kita apakah iterator telah meneruskan elemen terakhir dalam struktur data. Nilai done adalah true hanya saat panggilan ke next() mengakibatkan 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 generator atau menentukan ekspresi fungsi generator:

function* myGeneratorFunction() { };

Seperti iterator, fungsi generator mempertahankan status. Memanggil fungsi generator menampilkan objek Generator baru tetapi tidak langsung jalankan 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 setiap panggilan ke next() pada fungsi generator yang ditampilkan ditentukan oleh ekspresi yield, yang menjeda eksekusi fungsi generator dan mengembalikan nilai yang berisi kata kunci yield. Panggilan nanti ke next() melanjutkan eksekusi fungsi, berhenti sejenak pada ekspresi yield berikutnya dan yang menampilkan nilai yang 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), fungsi lainnya body dieksekusi, dan Objek yang ditampilkan memiliki value undefined dan done properti 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 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 return kata kunci. Metode ini kemudian menampilkan Objek ke konteks pemanggil 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, memungkinkan "komunikasi" dua arah dari dan kembali ke bagian yang ditangguhkan fungsi generator. Saat nilai diteruskan ke metode next() generator sebagai argumen, argumen ini menggantikan nilai yang terkait dengan argumen sebelumnya, Ekspresi yield:

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 sebelumnya yield, dan tidak hanya menetapkan ulang nilai yield sebelumnya ke nilai yang ditentukan dalam 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 untuk 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 lain, untuk melakukan iterasi dan menghasilkan setiap nilai operand-nya akan menampilkan:

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 pada dasarnya JavaScript bersifat sinkron. dalam eksekusinya, ada mekanisme yang memungkinkan pengembang untuk memanfaatkan loop peristiwa untuk menjalankan dan asinkron.

Promise

Promise adalah placeholder untuk nilai yang tidak diketahui kapan promise tersebut dibuat. Ini adalah kontainer yang menentukan operasi asinkron, persyaratan oleh operasi dianggap berhasil atau gagal, tindakan yang harus diambil dalam kedua kasus tersebut, dan nilai yang dihasilkan.

Membuat instance Promise menggunakan operator new dengan Promise bawaan fungsi konstruktor. Konstruktor ini menerima fungsi yang disebut executor sebagai argumen. Fungsi eksekutor itu biasanya digunakan untuk melakukan satu atau lebih tindakan asinkron, kemudian menentukan persyaratan yang akan digunakan untuk Promise dianggap berhasil dipenuhi atau ditolak. Promise didefinisikan sebagai pending saat fungsi eksekutor berjalan. Setelah eksekutor selesai, sebuah Promise dianggap terpenuhi (atau diselesaikan, dalam beberapa sumber dokumentasi) jika fungsi eksekutor dan tindakan asinkron yang dilakukannya selesai berhasil, dan ditolak jika fungsi eksekutor mengalami error, atau tindakan asinkron yang dilakukan akan gagal. Setelah Promise terpenuhi atau ditolak, hal tersebut 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 hasil Promise sebagai argumen (biasanya kesalahan karena 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 then(), catch(), dan Metode finally() diwarisi dari konstruktor Promise. Tiap-tiap metode akan menampilkan Promise, yang dapat segera ditindaklanjuti dengan then(), catch(), atau finally() lagi, memungkinkan Anda merantai Promise yang dihasilkan.

then() menyediakan dua fungsi callback sebagai argumen. Gunakan yang pertama untuk memenuhi Promise yang dihasilkan, dan yang kedua untuk menolaknya. Kedua metode menerima satu yang memberikan nilai pada Promise.

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 menangani status yang terpenuhi saja, dan catch untuk menangani status ditolak. Panggil catch dengan satu argumen yang berisi 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 finally akan dipanggil terlepas dari apakah Promise terpenuhi atau ditolak. Fungsi pengendali dipanggil tanpa argumen karena tidak dimaksudkan untuk dengan nilai yang diteruskan dari Promise, hanya untuk mengeksekusi kode setelah Promise selesai.

Serentak

Konstruktor Promise menyediakan empat metode untuk bekerja dengan beberapa metode terkait Promise, menggunakan iterable yang berisi objek Promise. Ini masing-masing menampilkan Promise, yang terpenuhi atau ditolak berdasarkan status dari Promise yang diteruskan kepadanya. Misalnya, Promise.all() membuat Promise yang hanya terpenuhi 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 konkurensi 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 hanya ditolak jika semua Promise ditolak.
Promise.allSettled()
Terpenuhi saat Promise telah selesai, terlepas dari hasilnya.
Promise.race()
Ditolak atau dipenuhi berdasarkan hasil dari Janji pertama yang harus diselesaikan, mengabaikan semua Promise yang ditetapkan nanti.

async/await

Saat Anda menggunakan kata kunci async sebelum deklarasi fungsi atau ekspresi fungsi, nilai apa pun nilai yang ditampilkan fungsi ditampilkan sebagai Promise yang terpenuhi yang berisi nilai tersebut dengan sejumlah nilai. Dengan demikian, Anda dapat menjalankan dan mengelola operasi asinkron menggunakan sebagai 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 ditampilkan sebagai Promise 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