Sterowanie przepływem

Przepływ kontrolny to kolejność, w jakiej interpreter JavaScript wykonuje działanie wyciągów. Jeśli skrypt nie zawiera instrukcji zmieniających jego przepływ, jest wykonywane od początku do końca, po jednym wierszu. Struktury kontrolne są służy do określenia, czy zbiór instrukcji został wykonany na podstawie określonego zestawu kryteriów, wielokrotnie wykonywać zestaw instrukcji lub przerywać ciągu instrukcji.

Instrukcje warunkowe określają, czy kod powinien być wykonywany na podstawie więcej warunków. Instrukcja warunkowa wykonuje zawarty w niej kod, jeśli powiązany warunek (lub zestaw warunków) zwraca wartość true. W przeciwnym razie czy kod został pominięty.

ifelse

Instrukcja if ocenia warunek w nawiasach pasujących do wyrażenia, które obserwuj. Jeśli warunek w nawiasie przyjmuje wartość true, w argumencie instrukcja lub blokada za pasującym do niej nawiasem jest wykonywany:

if ( true ) console.log( "True." );
> "True."

if ( true ) {
   
const myString = "True.";
    console
.log( myString );
}
> "True."

Jeśli warunek w nawiasie przyjmuje wartość false, wyrażenie, które jest ignorowany:

if ( false ) console.log( "True." );

Słowo kluczowe else bezpośrednio po instrukcji if i jej Warunkowo wykonywana instrukcja wskazuje instrukcję do wykonania, jeśli Warunek if przyjmuje wartość false:

if ( false ) console.log( "True." )''
else console.log( "False" );
> "False."

Aby połączyć kilka instrukcji if, możesz utworzyć warunkowo wykonana instrukcja po else innej instrukcji if:

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

Zdecydowanie zalecamy użycie składni instrukcji blokowych z uwzględnieniem warunków warunkowych, poprawia czytelność, ale klauzule else if są często wyjątkiem od tej reguły:

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 potrójny

if warunkowo wykonuje instrukcję. Operator potrójny (dokładniejszy ale rzadziej nazywany potrójnym operatorem warunkowym). warunkowego wykonania wyrażenia. Jak sama nazwa wskazuje, potrójny to jedyny operator JavaScript, który korzysta z trzech operandów:

  • Warunek do sprawdzenia, a po nim znak zapytania (?).
  • Wyrażenie, które ma zostać wykonane, jeśli warunek jest zwracany jako true, po którym następuje znak dwukropek (:).
  • Wyrażenie, które zostanie wykonane, jeśli warunek przyjmuje wartość false.

Często używa się go do warunkowania lub przekazywania wartości:

const myFirstResult  = true  ? "First value." : "Second value.";
const mySecondResult = false ? "First value." : "Second value.";

myFirstResult
;
> "First value."

mySecondResult
;
> "Second value."

switchcase

Używaj instrukcji switch do porównywania wartości wyrażenia z listą potencjalne wartości zdefiniowane za pomocą co najmniej jednego case słowa kluczowego. Ta składnia jest niespotykaną, ponieważ pochodzi ona z najwcześniejszych decyzji związanych z projektowaniem JavaScript. Składnia switch...case zawiera słowo kluczowe switch, a po nim wyrażenie ujęte w nawiasy, a po niej dopasowane nawiasy klamrowe. Treść switch może zawierać case słów kluczowych, zwykle jedno lub więcej, po którym następuje wyrażenie lub wartość oraz dwukropek (:).

Gdy interpreter osiągnie wartość case z wartością pasującą do wyrażenia oceniane w nawiasach po słowie kluczowym switch, wykonuje ono dowolne instrukcje po tej klauzuli case:

switch ( 2 + 2 === 4 ) {
 
case false:
    console
.log( "False." );
 
case true:
    console
.log( "True." );
}
> "True."

Wszystkie instrukcje po pasujących case są wykonywane, nawet jeśli są zawarte w instrukcji block.

switch ( 2 + 2 === 4 ) {
   
case false:
    console
.log( "False." );
 
case true:
    let myVariable
= "True.";
    console
.log( myVariable );

}
> "True."

Jedną z pułapek korzystania z funkcji switch…case jest to, że po znalezieniu dopasowania Interpreter JavaScript wykonuje dowolną instrukcję, która następuje po dopasowanej instrukcji case, nawet te zawarte w innych klauzulach case. Jest to tzw. „przełamanie”. do następny case:

switch ( 2 + 2 === 7 ) {
   
case false:
    console
.log( "False." );
 
case true:
    console
.log( "True." );
}
> "False."
> "True."

Aby tego uniknąć, zakończ każdy przypadek słowem kluczowym break, które natychmiast zatrzymuje ocenę treści switch:

switch ( 2 + 2 === 7 ) {
   
case false:
    console
.log( "False." );
   
break;
 
case true:
    console
.log( "True." );
   
break;
}
> "False."

Jeśli żaden element case nie pasuje do wartości warunkowej, funkcja switch wybiera default klauzuli, jeśli występuje:

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

Reklamy zastępcze mają jednak zastosowanie również do zdarzenia default, co może prowadzić do nieoczekiwane rezultaty. Aby rozwiązać ten problem, zakończ wyrażenie default wyrażeniem break, lub umieścić je na ostatnim miejscu na liście zgłoszeń.

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.

Ponieważ klauzule case nie wymagają parametru Instrukcja block do grupowania jeśli jest wiele instrukcji, klauzule case i default nie tworzą zakres leksykalny samodzielnie:

let myVariable;
switch ( true ) {
 
case true:
    let myVariable
= "True.";
   
break;
 
default:
    let myVariable
= "False.";
   
break;
}
> Uncaught SyntaxError: redeclaration of let myVariable

Aby zarządzać zakresem, użyj instrukcji blokowania:

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

Pętle i iteracja

pętle umożliwiają powtarzanie zestawu instrukcji tak długo, jak długo spełniony jest warunek; do momentu spełnienia warunku. Wykonuj zapętlone instrukcje zestawu instrukcji do uzyskania określonego wyniku lub do momentu, gdy tłumacz dochodzi do końca iteracyjnej struktury danych (np. ostatniego elementu w argumencie tablica, mapa lub zbiór, końcowa właściwość obiektu lub ostatni znak w argumencie ciąg znaków).

Pętle przecinają ruch „od góry do dołu” przebieg wykonania skryptu przez iterację w zbiorze instrukcji do momentu spełnienia co najmniej jednego z warunków lub w zależności od składni użytej do utworzenia pętli. Po zakończeniu pętli, będzie kontynuowane do następujących po nim instrukcji. W poniższym przykładzie instrukcje w treści pętli są wykonywane 3 razy przed tłumacz przechodzi dalej:

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

Jeśli warunki nie zostaną spełnione podczas wykonywania pętli, pętla będzie kontynuowana bez ograniczeń czasowych. Te pętle nieskończone to typowa pułapka programowania, która może powoduje główny wątek wykonania. aby wstrzymać ją na stałe, a nawet zawiesić kartę przeglądarki.

Ten przykład jest wykonywany tak długo, jak pozostaje wartość logiczna true true Wartości logiczne są stałe, spowoduje to nieskończoną pętlę.

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

Unikaj pozostawiania w kodzie produkcyjnym nieskończonych zapętleń. Jeśli przypadkowo utworzysz w trakcie programowania, możesz rozwiązać problem, zamykając kartę przeglądarki, w której działa. zaktualizowanie kodu, tak aby pętla nie była już nieskończona, i ponowne uruchomienie stronę.

while

Pętla while jest tworzona za pomocą słowa kluczowego while, po którym następuje parę w nawiasach znajduje się warunek do sprawdzenia. Jeśli określony warunek przyjmuje początkowo wartość true, wyrażenie (lub instrukcja blokowa), która następuje po nawiasy są wykonywane. W przeciwnym razie pętla nigdy się nie uruchamia. Po każdym powtórzenia, warunek jest sprawdzany ponownie, a jeśli ma on nadal wartość true, pętla i powtarzania.

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

Jeśli tłumacz znajdzie instrukcję continue w pętli while, zatrzymuje ją powtórzenia, ponownie ocenia warunek i, jeśli to możliwe, kontynuuje pętlę:

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

Jeśli tłumacz znajdzie instrukcję break w pętli while, oznacza to, że iteracja zatrzymuje się, a warunek nie jest sprawdzany ponownie, dzięki czemu tłumacz może przejść dalej:

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

Za pomocą while możesz wykonać określoną liczbę powtórzeń, tak jak widać w w poprzednim przykładzie, ale najczęstszym przypadkiem użycia funkcji while jest pętla nieokreślona długość:

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 to wariant pętli while, w której funkcja warunkowa ocena ma miejsce na końcu każdej iteracji pętli. Oznacza to, że treść pętli jest zawsze wykonywana co najmniej raz.

Aby utworzyć pętlę do...while, użyj słowa kluczowego do, a po nim instrukcji (lub instrukcji blokowania), wykonywane przy każdej iteracji pętli. Bezpośrednio po tym wyrażeniu dodaj while i dopasowane nawiasy zawierające warunek do sprawdzenia. Kiedy ten warunek nie jest już zwracany jako true, pętla się kończy.

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

Podobnie jak w przypadku pętli while, najczęstszym przypadkiem użycia funkcji do...while jest pętla nieokreślona długość:

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

Użyj pętli for, aby iterować ponad znaną ilość. W starszych bazach kodu było to często używane do iteracji elementów w tablicy.

Aby utworzyć pętlę for, użyj słowa kluczowego for, a następnie zestawu nawiasów które akceptuje 3 poniższe wyrażenia w kolejności i oddzielone znakiem średniki:

  1. Wyrażenie do sprawdzenia po rozpoczęciu pętli
  2. Warunek określający, czy pętla ma być kontynuowana
  3. wyrażenie do wykonania na końcu każdej pętli.

Po nawiasach dodaj wyrażenie (zwykle instrukcja blokowa), która ma być wykonywane w trakcie pętli.

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

Pierwsze wyrażenie inicjuje zmienną, która działa jak licznik. Ten jest oceniane raz przed pierwszą iteracją pętli. Dostępne opcje zainicjuj tę zmienną za pomocą algorytmu let (lub wcześniej var) jak dowolnego innego zmienną, a jej zakresem jest treść pętli. Te zmienne mogą mieć dowolne prawidłowy, ale często nazywa się go i ze względu na „iteracja” czy „indeks”. Wydaje się to sprzeczne z przyjętym sprawdzone metody tworzenia przewidywalnych nazw identyfikatorów, ale konwencja jest na tyle dobrze ugruntowana, aby inni programiści mogli wystarczy spojrzeć. Ponieważ zindeksowane kolekcje nie są indeksowane, te zmienne niemal zawsze mają początkową wartość 0.

Podobnie jak w przypadku innych form pętli, warunek jest wyrażeniem określającym, czy pętla ma zostać wykonana. Najczęściej używa się go do ustawienia górna część dla licznika iteracji. Tłumacz ocenia warunek przed wykonując pętlę for po raz pierwszy.Jeśli warunek nie zostanie do true, treść pętli nie jest wykonywana.

Ostatnie wyrażenie jest wykonywane na końcu każdej iteracji w pętli. Zwykle służy do zwiększania identyfikatora o 1.

Najczęściej występujące pętle for powtarzają się przez tablice w starszych wersjach w postaci baz kodu. W tych przypadkach warunek kontynuacji pętli jest liczba iteracji mniejsza od lub równa długości iterowanej tablicy przez telefon. Zmienna używana do śledzenia bieżącej liczby iteracji jest używana do wyszukiwania wartość powiązaną z tym indeksem w tablicy, umożliwiając każdemu elementowi tablicę, na której należy podjąć działanie w kolejności:

var myArray = [ true, false, true ];
for( let i = 0; i <= myArray.length; i++ ) {
  console
.log( myArray[ i ] );
}
> true
> false
> true

Takie podejście przestało być wykorzystywane i zaczęło obowiązywać bardziej nowoczesne podejście do zapętlanie się iteracjonalnych struktur danych.

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

Pętle for...of... umożliwiają iterację wartości zapisanych w elastyczną strukturę danych, np. tablica, zbiór czy mapa.

Pętla for...of... używa słowa kluczowego for, po którym następuje zestaw nawiasów zawiera zmienną, a po niej ciąg of i powtarzaną strukturę danych ponad. Zmienną może być deklaracją wykonaną tutaj za pomocą funkcji let, const lub var, zmienna zadeklarowana wcześniej w bieżącym zakresie, obiekt usługi lub przypadkiem wystąpienia niszczenie przypisania. Zawiera wartość elementu, który odpowiada bieżącej iteracji w pętli.

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

W tym przykładzie użycie parametru const w parametrze myElement działa, mimo że myElement to otrzymuje nową wartość w każdej iteracji pętli. To dlatego, że zmienne zadeklarowane za pomocą funkcji let lub const mają zakres ograniczony do instrukcji block w w pętli. Zmienna jest inicjowana na początku każdej iteracji i usuwana na na końcu tej iteracji.

for...in...

Używaj pętli for...in... do iteracji według wyliczanych właściwości obiektu, również wszystkie odziedziczone właściwości. Podobnie jak w przypadku pętli for...of..., for...in... pętla używa słowa kluczowego for, po którym następuje zestaw nawiasów zawierającej zmienną zawierającą wartość klucza właściwości odpowiadającego z bieżącą iteracją pętli. Po tej zmiennej następuje określenie in, a następnie powtarzany obiekt:

const myObject = { "myProperty" : true, "mySecondProperty" : false };
for( const myKey in myObject ) {
  console
.log( myKey );
}
> "myProperty"
> "mySecondProperty"

I znów, mimo że wartość myKey zmienia się z każdym wystąpieniem pętli, możesz użyć zmiennej const bez błędów, ponieważ zmienna jest w praktyce odrzucana na końcu każdej iteracji, a potem odtworzyć na początku.

Wartość powiązana z każdym kluczem usługi nie jest bezpośrednio dostępna dla for...in... składnia. Ponieważ jednak pętla ma dostęp do klucza właściwości w przy każdej iteracji możesz użyć tego klawisza jego wartość:

const myObject = { "myProperty" : true, "mySecondProperty" : false };
for( const myKey in myObject ) {
 
const myValue = myObject[ myKey ];
  console
.log( `${ myKey } : ${ myValue }` );
}
> "myProperty : true"
> "mySecondProperty : false"

Właściwości odziedziczone z wbudowanych konstruktorów są niewyliczalne, co oznacza, że for...in... nie wykonuje iteracji za pomocą właściwości dziedziczonych z Object za pomocą konstruktora. Jednak wszystkie wymienne właściwości w argumencie łańcuch prototypów:

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 udostępnia wbudowane metody określania, czy usługa jest bezpośrednią właściwość obiektu, a nie właściwości prototypu obiektu; łańcuch: nowoczesny Object.hasOwn() i starszych metod Object.prototype.hasOwnProperty(). Te metody sprawdzają, czy określona właściwość jest dziedziczona (czy niezadeklarowana), zwracanie funkcji true tylko w przypadku natychmiastowych właściwości określonego obiektu:

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"

Istnieją również trzy metody statyczne, które zwracają tablicę składającą się z klucze wyliczalne (Object.keys()), wartości (Object.values()) lub pary klucz-wartość (Object.entries()):

const myObject = { "myProperty" : true, "mySecondProperty" : false };
Object.keys( myObject );
> Array [ "myProperty", "mySecondProperty" ]

Umożliwia to powtarzanie kluczy, wartości lub par klucz-wartość obiektu (za pomocą niszczenia przypisania) bez uwzględniania właściwości należących do prototypu tego obiektu:

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()

metody forEach() udostępniane przez tablica, Mapa, Plan, a konstruktory NodeList to przydatny skrót do iteracji danych w kontekście funkcji wywołania zwrotnego. W przeciwieństwie do innych form pętli pętla utworzona za pomocą dowolnej metody forEach() nie może zostać przerwana za pomocą metody break lub continue

forEach to metoda należąca do prototypu każdej struktury danych. Co forEach oczekuje funkcji wywołania zwrotnego jako argumentu, chociaż różni się ona nieco w argumentach uwzględnionych przy wywołaniu tej funkcji. Druga, opcjonalna określa wartość this, która ma być używana jako kontekst wywołania dla funkcji funkcji wywołania zwrotnego.

Funkcja wywołania zwrotnego używana z Array.forEach udostępnia parametry zawierające wartość bieżącego elementu, indeks bieżącego elementu oraz tablicę, dla której została wywołana metoda forEach:

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 ]

Funkcja wywołania zwrotnego używana z Map.forEach udostępnia parametry zawierające wartość powiązaną z bieżącym elementem, klucz powiązany z bieżącym elementem , a metoda Mapa forEach została wywołana w dniu:

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 }

Wywołanie zwrotne Set.forEach zawiera podobne parametry. Ponieważ zestaw nie zawiera indeksów lub kluczy różniących się od wartości, drugi argument zamiast tego zapewnia zbędnej, nieistotnej wartości, ściśle aby zachować spójność składni innych metod forEach.

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 ]

Iteratory

Eterowalność to dowolna struktura danych składająca się z pojedynczych elementów, które można i dokonania kolejnych iteracji przy użyciu metod opisanych wcześniej. iterator to element możliwy do powtórzenia obiekt zgodny z protokołem iteratora, co oznacza, że musi implementować metody next(), która przechodzi kolejne elementy w elementach, które zawiera, przy każdym wywołaniu tej metody zwracany jest obiekt dla każdej sekwencji w określonym formacie.

wbudowane iteracyjne struktury danych JavaScript (takie jak tablica, Mapa oraz Set) nie są iteratorami w same, ale wszystkie dziedziczą metodę iterator, dostępną za pomocą @@iterator znany symbol, , który zwraca obiekt iteratora utworzony na podstawie struktury danych iteracyjnej:

const myIterable = [ 1, 2, 3 ];
const myIterator = myIterable[ Symbol.iterator ]();

myIterable
;
> (3) [1, 2, 3]

myIterator
;
> Array Iterator {}

Wywołanie metody next() w iteratorze powoduje przejście przez zawarte w niej elementy zawiera po jednym naraz, przy czym każde wywołanie zwraca obiekt zawierający dwa właściwości: value, który zawiera wartość bieżącego elementu, oraz done, jest to wartość logiczna wskazująca, czy iterator przekazał ostatni element w argumencie do struktury danych. Wartość done wynosi true tylko w przypadku wywołania next() powoduje próbę uzyskania dostępu do elementu poza ostatnim elementem lub iteratorem.

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 }

Funkcje generatora

Zadeklaruj generator, używając słowa kluczowego function* (zwróć uwagę na gwiazdkę) lub zdefiniuj wyrażenie funkcji generatora:

function* myGeneratorFunction() { };

Podobnie jak iteratorzy funkcje generatorów zachowują stan. Wywołuję generator zwraca nowy obiekt generatora, ale nie od razu uruchom kod w treści funkcji:

function* myGeneratorFunction() {
  console
.log( "Generator function body ")
};
const myGeneratorObject = myGeneratorFunction();

myGeneratorObject
;
> Generator {  }

typeof myGeneratorObject;
> "object"

Obiekty generatorów są zgodne z protokołem iteratora. Wartość każdego wywołania Wartość next() w przypadku zwracanej funkcji generatora jest określona przez wyrażenie yield, wstrzymujące wykonanie funkcji generatora i zwracające wartość funkcji generatora, zawierające słowo kluczowe yield. Późniejsze połączenia z numerem next() kontynuuj wykonywanie funkcji, wstrzymując się przy następnym wyrażeniu yield, a następnie zwracającą powiązaną wartość.

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 }

Jeśli funkcja next() zostanie wywołana po nieokreśleniu kolejnych wartości za pomocą yield, return lub throw (w przypadku błędu) pozostałą część funkcji treść jest wykonywana, a zwrócony obiekt ma value o wartości undefined i done właściwość 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 }

Używaj next() tylko w przypadku obiektu zwracanego przez funkcję generatora, a nie generatora. W przeciwnym razie każde wywołanie funkcji generatora tworzy nowy obiekt generatora:

function* myGeneratorFunction() {
 
yield "First";
 
yield "Second";
};

myGeneratorFunction
().next();
> Object { value: "First", done: false }

myGeneratorFunction
().next();
> Object { value: "First", done: false }

Tak jak w przypadku każdej funkcji, funkcja generatora zatrzymuje się po napotkaniu funkcji return. słowa kluczowego. Następnie zwraca obiekt do kontekstu wywołania zawierającego zwróciło wartość i właściwość done o wartości 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 }

Wyrażenie yield może przyjmować część semantyki identyfikatora, dwukierunkowa „komunikacja” z i z powrotem do zawieszonej części generatora. Kiedy wartość jest przekazywana do metody next() generatora jako zamiast argumentu, zastępuje wartość powiązaną z poprzednim, zawieszonym Wyrażenie 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 }

Pamiętaj, że spowoduje to zastąpienie całego wyrażenia powiązanego z poprzedni yield i nie tylko ponownie przypisuje wartość poprzedniego elementu yield do wartość określona we właściwości 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 }

Każdy argument przekazany do pierwszego wywołania funkcji next() jest ignorowany, ponieważ nie ma poprzedniego wyrażenia yield, aby zaakceptować tę wartość. Tak jak w przypadku każdej innej funkcji, dostępne są argumenty przekazywane do początkowego wywołania funkcji generatora zakres treści funkcji generatora:

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* (zwróć uwagę na gwiazdkę) jest używany z iteracjami, w tym: innej funkcji generatora do iteracji i uzyskania każdej wartości argumentu zwraca:

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 }

Asynchroniczny JavaScript

Mimo że JavaScript jest zasadniczo synchroniczny to istnieją mechanizmy, które pozwalają programistom wykorzystać pętlę zdarzeń, która ma zostać wykonana asynchronicznych zadań.

Obietnice

Obietnica to obiekt zastępczy dla wartości, która nie jest znana, gdy obietnica jest Utworzono. To kontener, który dyktuje operację asynchroniczną, działania, które zostaną uznane za sukces lub niepowodzenie, w obu przypadkach i uzyskaną wartość.

Tworzenie instancji Promise za pomocą operatora new z wbudowanym Promise za pomocą funkcji konstruktora. Ten konstruktor akceptuje funkcję o nazwie wykonawca jako argumentu. Ta funkcja wykonawcy jest zwykle wykorzystywana do wykonywania asynchronicznych działań, a następnie dyktując warunki, według których powinna zostać złożona obietnica uznanych za zrealizowane lub odrzucone. Obietnica jest określona jako oczekująca. gdy jest uruchomiona funkcja wykonawcy. Po zakończeniu realizacji przez wykonawcy obietnica jest uznawany za zrealizowany (lub rozwiązany w niektórych źródłach dokumentacji), jeśli funkcja wykonawcy i wykonywane przez nią działanie asynchroniczne zostały zakończone i odrzucona, jeśli funkcja wykonawcy napotka błąd; wykonywane działanie asynchroniczne kończy się niepowodzeniem. Po spełnieniu obietnicy lub odrzucono, jest uznawane za rozstrzygnięte.

const myPromise = new Promise( () => { });

Konstruktor wywołuje funkcję wykonawcy za pomocą dwóch argumentów. Te argumenty to funkcje, które pozwalają ręcznie zrealizować lub odrzucić obietnicę:

const  myPromise = new Promise( ( fulfill, reject ) => { });

Funkcje służące do realizacji lub odrzucenia obietnicy są wywoływane z wynikiem wartość obietnicy jako argument (zwykle jest to błąd odrzucenia):

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

Łańcuch obietnic

Na powstałym obiekcie Promise można wykonać działania za pomocą interfejsów then(), catch() i Metody finally() odziedziczone z konstruktora Promise. Każda z tych opcji zwraca obietnicę, dla której można natychmiast wykonać działanie za pomocą funkcji then(), catch() lub finally(), co pozwoli Ci połączyć wynikowe obietnice.

Funkcja then() udostępnia 2 funkcje wywołania zwrotnego jako argumenty. Wykorzystaj pierwsze z nich do realizacji wynikową obietnicę, a drugi ją odrzucić. Obie metody , który nadaje wynikowi Promise wartość.

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

Za pomocą polecenia then() możesz też obsługiwać tylko stan realizacji, a catch – do obsługi stanu odrzucenia. Wywołaj funkcję catch za pomocą jednego argumentu zawierającego wartość podana w metodzie odrzucenia obietnicy:

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

W przeciwieństwie do funkcji then i catch, które umożliwiają uruchamianie funkcji obsługi, gdy obietnica zostanie wypełnione lub odrzucone, funkcja przekazana jako argument do funkcji finally jest wywoływana niezależnie od tego, czy obietnica została spełniona czy odrzucona. Funkcja obsługi jest wywoływana bez argumentów, ponieważ nie jest przeznaczona pracować z wartościami przekazanymi z obietnicy, tylko do wykonywania kodu po parametrze Obietnica skończona.

Równoczesność

Konstruktor Promise udostępnia 4 metody pracy z wieloma powiązanymi Obietnice – użycie parametru iterable zawierającego obiekty Promise. Te każda z tych metod zwraca obietnicę, która jest realizowana lub odrzucana na podstawie stanu z przekazanych mu obietnic. Na przykład Promise.all() tworzy obietnicę którą zostanie spełniony tylko wtedy, gdy zostanie spełniony każda obietnica przekazywana do tej metody:

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

Metody Promise równoczesności są następujące:

Promise.all()
Zrealizowany tylko wtedy, gdy wszystkie podane obietnice zostaną spełnione.
Promise.any()
Wypełniona, jeśli którakolwiek z podanych obietnic zostanie spełniony, a odrzucona tylko jeśli wszystkie obietnice zostaną odrzucone.
Promise.allSettled()
Zrealizowane, gdy obietnice ustabilizują się, niezależnie od ich wyniku.
Promise.race()
Odrzucona lub zrealizowana na podstawie wyniku pierwszej obietnicy do rozliczenia, ignorując wszystkie obietnice zawarte w późniejszym czasie.

async/await

Jeśli używasz słowa kluczowego async przed deklaracją funkcji lub wyrażenie funkcji, dowolny zwracana przez funkcję wartość jest jako spełniona obietnica zawierająca . Dzięki temu możesz uruchamiać operacje asynchroniczne i zarządzać nimi przy użyciu w procesie programowania synchronicznego.

async function myFunction() {
 
return "This is my returned value.";
}

myFunction
().then( myReturnedValue => console.log( myReturnedValue ) );
> "This is my returned value."

Wyrażenie await wstrzymuje wykonywanie funkcji asynchronicznej, podczas gdy wiążąca obietnica jest wiążąca. Po uzgodnieniu obietnicy wartość wyrażenie await to zrealizowana lub odrzucona wartość obietnicy.

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

Każda wartość inna niż obietnica zawarta w wyrażeniu await jest zwracana jako spełniona obietnica:

async function myFunction() {
 
const myPromisedResult = await "String value.";
 
return myPromisedResult;
}

myFunction
()
 
.then( myResult => console.log( myResult ) )
 
.catch( myFailedResult => console.error( myFailedResult ) );
> "String value."

Sprawdź swoją wiedzę

Jakiego rodzaju pętli używasz, aby iterować według znanej ilości?

do...while
for
while