Flusso di controllo

Il flusso di controllo è l'ordine in cui l'interprete JavaScript viene eseguito istruzioni. Se uno script non include istruzioni che alterano il flusso, vengono eseguiti dall'inizio alla fine, una riga alla volta. Le strutture di controllo sono utilizzata per determinare se un insieme di istruzioni viene eseguito o meno in base a un set di criteri definito, eseguire un insieme di istruzioni ripetutamente o interrompere un una sequenza di istruzioni.

Le istruzioni condizionali determinano se il codice deve essere eseguito in base a uno o più condizioni. Un'istruzione condizionale esegue il codice che contiene se la condizione associata (o l'insieme di condizioni) è valutata come true. In caso contrario, viene ignorato.

ifelse

Un'istruzione if valuta una condizione tra parentesi corrispondenti seguire. Se la condizione tra parentesi restituisce true, il valore l'istruzione o l'istruzione di blocco che segue le parentesi corrispondenti:

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

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

Se la condizione tra parentesi restituisce false, l'istruzione che viene ignorato:

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

Una parola chiave else subito dopo un'istruzione if e la relativa l'istruzione eseguita in modo condizionale specifica l'istruzione da eseguire se La condizione if restituisce false:

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

Per concatenare più istruzioni if, puoi creare il metodo istruzione eseguita in modo condizionale dopo else un'altra istruzione if:

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

Consigliamo vivamente di utilizzare la sintassi delle istruzioni di blocco seguendo le condizionali per migliorare la leggibilità, ma le clausole else if costituiscono spesso un'eccezione a questo:

if ( myCondition === 5 ) {
    console
.log( "Five." );
} else if ( myCondition === 3 ) {
    console
.log( "Three" );
} else {
    console
.log( "Neither five nor three." );
}
> "Neither five nor three."

Operatore ternario

if esegue un'istruzione in modo condizionale. L'operatore ternario (più precisamente ma meno comunemente chiamato operatore condizionale ternario) è un termine abbreviato eseguire un'espressione in modo condizionale. Come suggerisce il nome, il ternario è l'unico operatore JavaScript che utilizza tre operandi:

  • Una condizione da valutare, seguita da un punto interrogativo (?).
  • L'espressione da eseguire se la condizione restituisce true, seguita da un I due punti (:).
  • L'espressione da eseguire se la condizione restituisce false.

Viene spesso utilizzato per impostare o trasmettere un valore in modo condizionale:

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

myFirstResult
;
> "First value."

mySecondResult
;
> "Second value."

switchcase

Utilizza l'istruzione switch per confrontare il valore di un'espressione con un elenco di valori potenziali definiti utilizzando una o più parole chiave case. Questa sintassi è insolito perché deriva da alcune delle prime decisioni di progettazione di JavaScript. La sintassi switch...case utilizza la parola chiave switch, seguita da un'espressione per essere valutata racchiusa tra parentesi, seguita da una coppia di parentesi graffe corrispondenti. Il corpo dell'elemento switch può contenere case parole chiave, in genere una o più, seguiti da un'espressione o da un valore, seguiti dai due punti (:).

Quando l'interprete raggiunge un case con un valore corrispondente all'espressione valutato tra parentesi dopo la parola chiave switch, esegue qualsiasi istruzioni che seguono la clausola case:

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

Tutte le istruzioni che seguono l'elemento case corrispondente vengono eseguite, anche se racchiusa in un'istruzione di blocco.

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

}
> "True."

Un'insidia di utilizzare switch…case è che, dopo aver trovato una corrispondenza, L'interprete JavaScript esegue qualsiasi istruzione che segue il case corrispondente, anche quelle all'interno delle altre clausole case. Questo fenomeno è chiamato "passaggio attraverso" alle case successivo:

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

Per evitare le fall-through, termina ogni caso con la parola chiave break, che interrompe immediatamente la valutazione del corpo switch:

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

Se nessun case corrisponde al valore condizionale, switch seleziona default , se presente:

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

Tuttavia, le azioni di fall-through si applicano anche a default, il che può portare a risultati imprevisti. Per risolvere il problema, termina l'estratto conto default con break, o alla fine dell'elenco delle richieste.

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.

Poiché le clausole case non richiedono una istruzione di blocco per raggruppare più istruzioni, le clausole case e default non creano ambito lessicale in sé:

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

Per gestire l'ambito, utilizza le istruzioni di blocco:

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

Loop e iterazione

I loop ti consentono di ripetere un insieme di istruzioni finché una condizione è soddisfatta, oppure fino a quando non viene soddisfatta una condizione. Utilizza i loop per eseguire un insieme di istruzioni di volte, fino a quando non si raggiunge un risultato specifico o fino a quando raggiunge la fine di una struttura di dati iterabile (ad esempio, l'elemento finale un array, una mappa o un insieme, la proprietà finale di un oggetto o l'ultimo carattere una stringa).

I loop interrompono l'"alto verso il basso" il flusso di esecuzione di uno script in un insieme di istruzioni finché una o più condizioni non sono soddisfatte oppure non sono più soddisfatti, a seconda della sintassi utilizzata per creare il loop. Al termine del loop, continua con le istruzioni che la seguono. Nell'esempio seguente, le istruzioni nel corpo del loop vengono eseguite tre volte prima Interprete passa a:

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

Se le condizioni non possono essere soddisfatte durante l'esecuzione del loop, il loop continua a tempo indeterminato. Questi cicli infiniti sono un comune insidiamento di programmazione che può causano il thread di esecuzione principale mettere in pausa a tempo indeterminato o persino causare l'arresto anomalo di una scheda del browser.

L'esempio seguente viene eseguito finché il valore booleano true rimane true. Poiché i valori booleani sono immutabili, questo crea un ciclo infinito.

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

Evita di lasciare loop infiniti nel codice di produzione. Se crei accidentalmente uno durante lo sviluppo, puoi risolverlo chiudendo la scheda del browser in esecuzione aggiornare il codice in modo che il loop non sia più infinito e riaprire .

while

Viene creato un loop while utilizzando la parola chiave while seguita da una coppia di le parentesi con corrispondenze contenenti una condizione da valutare. Se viene specificato restituisce inizialmente true, l'istruzione (o l'istruzione di blocco) che segue vengono eseguite queste parentesi. In caso contrario, il loop non viene mai eseguito. Dopo ogni la condizione viene rivalutata e, se è ancora true, il loop si ripete.

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

Se l'interprete trova un'istruzione continue in un ciclo while, interrompe l'operazione l'iterazione, rivaluta la condizione e, se possibile, continua il loop:

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

Se l'interprete trova un'istruzione break in un ciclo while, tale iterazione si interrompe e la condizione non viene rivalutata, lasciando che l'interprete possa proseguire:

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

Puoi utilizzare while per ripetere un numero specifico di volte, come mostrato in nell'esempio precedente, ma il caso d'uso più comune di while è un loop di durata indeterminata:

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 è una variante del loop while in cui la condizione condizionale la valutazione avviene alla fine di ogni iterazione del loop. Ciò significa che il corpo del loop viene sempre eseguito almeno una volta.

Per creare un loop do...while, utilizza la parola chiave do seguita dall'istruzione (o l'istruzione di blocco) da utilizzare a ogni iterazione del loop. Subito dopo questa affermazione, aggiungi while e parentesi corrispondenti contenenti la condizione da valutare. Quando questa condizione non restituisce più true e il ciclo termina.

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

Come per un loop while, il caso d'uso più comune per do...while è un loop di durata indeterminata:

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

Utilizza i loop for per eseguire l'iterazione su una quantità nota. Nei codebase precedenti, questo era spesso utilizzato per iterare gli elementi di un array.

Per creare un loop for, utilizza la parola chiave for, seguita da una serie di parentesi che accetta le tre espressioni seguenti in ordine e separate da Punti e virgola:

  1. Un'espressione da valutare all'inizio del ciclo
  2. Una condizione che determina se il loop deve continuare
  3. Un'espressione da eseguire alla conclusione di ogni ciclo

Dopo queste parentesi, aggiungi l'istruzione (di solito un istruzione di blocco) per essere eseguite durante il loop.

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

La prima espressione inizializza una variabile che agisce come contatore. Questo viene valutata una volta, prima della prima iterazione del loop. Puoi inizializza questa variabile utilizzando let (o var, storicamente) come qualsiasi altra e la sua portata è il corpo del loop. Queste variabili possono avere qualsiasi identificatore valido, ma vengono spesso chiamati i per "iterazione" o "indice". Ciò sembra in contraddizione con quanto stabilito best practice per i nomi degli identificatori prevedibili, ma la convenzione è abbastanza consolidata da essere chiara agli altri sviluppatori di a colpo d'occhio. Poiché le raccolte indicizzate non sono indicizzate, queste variabili hanno quasi sempre un valore iniziale di 0.

Come per altre forme di loop, la condizione è un'espressione che determina se il loop deve essere eseguito. Viene utilizzato principalmente per impostare un valore per il contatore di iterazioni. L'interprete valuta la condizione prima eseguendo il loop for per la prima volta.Se inizialmente la condizione non viene restituisce true, il corpo del loop non viene eseguito.

L'espressione finale viene eseguita alla fine di ogni iterazione tramite il loop. In genere viene utilizzato per incrementare l'identificatore di uno.

In genere, vedrai for loop che si ripeteranno tramite array nelle versioni precedenti codebase. In questi casi, la condizione specificata per continuare il loop è una numero di iterazioni inferiore o uguale alla lunghezza dell'array da ripetere fino alla fine. La variabile utilizzata per monitorare il conteggio delle iterazioni corrente viene utilizzata per esaminare il valore associato a quell'indice nell'array, consentendo a ogni elemento di l'array su cui agire nell'ordine indicato:

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

Questo approccio è caduto a favore di approcci più moderni per loop attraverso strutture di dati iterabili.

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

Utilizza i loop for...of... per eseguire l'iterazione dei valori memorizzati in un struttura di dati iterabile, come un array, un set o una mappa.

Un ciclo for...of... utilizza la parola chiave for seguita da una serie di parentesi contenente una variabile, seguita da of, quindi la struttura dei dati che viene iterata oltre. La variabile può essere una dichiarazione eseguita qui utilizzando let, const o var, una variabile dichiarata in precedenza nell'ambito attuale, un oggetto o un'istanza distruggere il compito. Contiene il valore dell'elemento che corrisponde all'iterazione corrente del ciclo.

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

In questo esempio, l'utilizzo di const per myElement funziona anche se myElement è un nuovo valore in ogni iterazione del ciclo. Questo perché le variabili dichiarati con let o const rientrano nell'ambito dell'istruzione di blocco all'interno del ciclo. La variabile viene inizializzata all'inizio di ogni iterazione e rimossa alla fine di quell'iterazione.

for...in...

Usa i loop for...in... per ripetere le proprietà enumerabili di un oggetto, incluse le proprietà ereditate enumerabili. Come per un ciclo for...of..., una Il ciclo for...in... utilizza la parola chiave for seguita da una serie di parentesi una variabile contenente il valore della chiave di proprietà corrispondente con l'iterazione attuale del loop. Questa variabile è seguita dalla variabile in parola chiave, quindi l'oggetto di cui eseguire l'iterazione:

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

Anche in questo caso, nonostante il valore di myKey cambi a ogni iterazione del loop, puoi utilizzare const senza errori perché la variabile viene eliminata in modo efficace alla fine di ogni iterazione, quindi ricreata all'inizio.

Il valore associato a ogni chiave di proprietà non è disponibile direttamente for...in... sintassi. Tuttavia, poiché il loop ha accesso a una chiave della proprietà ogni iterazione, puoi usare questa chiave per "cercare" il suo valore:

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

Le proprietà ereditate dai costruttori integrati non sono enumerabili, for...in... non esegue l'iterazione attraverso le proprietà ereditate da Object come costruttore. Tuttavia, le eventuali proprietà enumerabili all'interno del parametro catena di prototipi:

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 offre metodi integrati per determinare se una proprietà è un proprietà diretta dell'oggetto anziché una proprietà sul prototipo dell'oggetto di una catena: il moderno Object.hasOwn() e i metodi Object.prototype.hasOwnProperty() precedenti. Questi valutano se una proprietà specificata è ereditata (o non dichiarata), che restituisce true solo per le proprietà immediate di un oggetto specificato:

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"

Esistono anche tre metodi statici, ognuno dei quali restituisce un array composto da un Chiavi enumerabili (Object.keys()), valori (Object.values()) o coppie chiave-valore (Object.entries()):

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

Questo ti consente di ripetere le chiavi, i valori o le coppie chiave-valore degli oggetti (utilizzando distruggere il compito) senza includere le proprietà del prototipo dell'oggetto:

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

I metodi forEach() forniti da Array, Mappa, Imposta, e i costruttori NodeList offrono un'abbreviazione utile per eseguire l'iterazione di un dato nel contesto di una funzione di callback. A differenza di altre forme di loop, un il loop creato con qualsiasi metodo forEach() non può essere interrotto utilizzando break o continue.

forEach è un metodo appartenente al prototipo di ogni struttura dati. Ogni forEach si aspetta una funzione di callback come argomento, anche se variano leggermente termini degli argomenti inclusi quando questa funzione viene chiamata. Un secondo, facoltativo, specifica un valore this da utilizzare come contesto di chiamata per l'elemento di chiamata.

La funzione di callback utilizzata con Array.forEach fornisce parametri contenenti il valore dell'elemento corrente, l'indice dell'elemento corrente e l'array su cui è stato richiamato il metodo 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 ]

La funzione di callback utilizzata con Map.forEach fornisce parametri contenenti il parametro valore associato all'elemento corrente, la chiave associata all'elemento corrente e il flag Mappa il metodo forEach è stato richiamato su:

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 }

Un callback Set.forEach include parametri simili. Poiché Set non contiene di indici o chiavi diversi dai valori, il secondo argomento fornisce invece ridondante e ignorabile, rigorosamente per mantenere la sintassi coerente con altri metodi di 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 ]

Iteratori

Una struttura di dati iterabile è qualsiasi struttura di dati costituita da singoli elementi che possono essere usando gli approcci descritti in precedenza. Un iteratore è un oggetto iterabile che segue il protocollo iteratore, il che significa che deve implementare un metodo next() che avanza tra gli elementi che contiene, uno alla volta, ogni volta che viene chiamato questo metodo, restituendo un oggetto per ogni sequenza in un formato specifico.

Le strutture di dati iterabili integrate di JavaScript (come Array, Map e Set) non sono iteratori in e di ma tutti ereditano un metodo iterator, accessibile tramite @@iterator simbolo molto noto, che restituisce un oggetto Iterator creato dalla struttura di dati iterabile:

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

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

myIterator
;
> Array Iterator {}

Chiamare il metodo next() su un iteratore passaggi attraverso gli elementi ne contiene uno alla volta e ogni chiamata restituisce un oggetto contenente proprietà: value, che contiene il valore dell'elemento corrente, e done, un valore booleano che indica se l'iteratore ha passato l'ultimo elemento in la struttura dei dati. Il valore di done è true solo in caso di chiamata a next() comporta il tentativo di accedere a un elemento oltre l'ultimo elemento in come un iteratore.

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 }

Funzioni generatore

Usa la parola chiave function* (nota l'asterisco) per dichiarare un generatore o definisci un'espressione di funzione del generatore:

function* myGeneratorFunction() { };

Come i iteratori, le funzioni generatore mantengono lo stato. Chiamata a un la funzione generatore restituisce un nuovo oggetto generatore, ma non lo restituisce immediatamente esegui il codice nel corpo della funzione:

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

myGeneratorObject
;
> Generator {  }

typeof myGeneratorObject;
> "object"

Gli oggetti generatore seguono il protocollo iteratore. Il valore a cui ogni chiamata next() su una funzione del generatore restituisce i risultati da un'espressione yield, che mette in pausa l'esecuzione della funzione di generatore e restituisce il valore del parametro che contiene la parola chiave yield. Chiamate successive a next() continuare l'esecuzione della funzione, mettendo in pausa alla successiva espressione yield e che restituisce il valore associato.

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 }

Quando next() viene chiamato dopo che non sono stati specificati altri valori utilizzando yield, return o throw (in caso di errore), il resto della funzione viene eseguito e l'oggetto restituito ha un valore value di undefined e un done proprietà di 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 }

Utilizza next() solo sull'oggetto restituito dalla funzione del generatore, non nella della funzione di generatore di rete. Altrimenti, ogni chiamata alla funzione del generatore crea un nuovo oggetto generatore:

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

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

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

Come per qualsiasi funzione, la funzione del generatore si interrompe quando rileva un return parola chiave. Quindi restituisce un oggetto nel contesto di richiamo che contiene valore restituito e una proprietà done con il valore 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 }

Un'espressione yield può assumere parte della semantica di un identificatore, che consentono la "comunicazione" bidirezionale e tornare alla parte sospesa di IA generativa. Quando un valore viene passato al metodo next() di un generatore come un argomento, sostituisce il valore associato all'elemento precedente, Espressione 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 }

Ricorda che sostituisce l'intera espressione associata al parametro precedente yield e non riassegna il valore dell'elemento yield precedente a il valore specificato in 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 }

Qualsiasi argomento passato alla prima chiamata a next() viene ignorato, perché non sono presenti l'espressione yield precedente per accettare quel valore. Come per qualsiasi altra funzione, argomenti passati alla chiamata di funzione iniziale del generatore sono disponibili l'ambito del corpo della funzione del generatore:

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 }

L'operatore yield* (nota l'asterisco) viene utilizzato in combinazione con un iterabile, come un'altra funzione generatore, di ripetere e dare a ogni valore il suo operando restituisce:

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 asincrono

Sebbene JavaScript sia fondamentalmente sincrono, in esecuzione, esistono meccanismi che consentono agli sviluppatori di sfruttare il loop di eventi per eseguire le attività asincrone.

Promesse

Una promessa è un segnaposto per un valore che non è noto quando la promessa è è stato creato. È un container che detta un'operazione asincrona, i termini in cui l'operazione è considerata riuscita o non riuscita, le azioni da intraprendere in entrambi i casi e il valore risultante.

Crea un'istanza Promise utilizzando l'operatore new con Promise integrato della funzione costruttore. Questo costruttore accetta una funzione denominata esecutore come argomento. Questa funzione esecutore viene in genere utilizzata per eseguire uno o più azioni asincrone, quindi detta i termini in base ai quali la Promessa dovrebbe essere considerata completata o rifiutata. Una promessa viene definita come in attesa mentre è in esecuzione la funzione esecutore. Al termine dell'esecutore, viene inviata una è considerato completato (o risolto in alcune fonti della documentazione) se la funzione esecutore e l'azione asincrona che esegue correttamente e rifiutato se la funzione esecutore rileva un errore; oppure l'azione asincrona eseguita non riesce. Dopo che una promessa è stata soddisfatta o rifiutata, viene considerata risolta.

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

Il costruttore chiama la funzione esecutore con due argomenti. Questi argomenti sono funzioni che ti consentono di completare o rifiutare manualmente la Promessa:

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

Le funzioni utilizzate per soddisfare o rifiutare una promessa vengono chiamate con della promessa come argomento (in genere un errore di rifiuto):

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

Concatenamento delle promesse

È possibile agire sull'oggetto Promise risultante utilizzando then(), catch() e finally() ereditati dal costruttore Promise. Ciascuno di questi restituisce una promessa, su cui è possibile intervenire immediatamente con then(), catch() o di nuovo finally(), in modo da collegare le promesse risultanti.

then() fornisce due funzioni di callback come argomenti. Utilizza il primo per completare la promessa risultante e il secondo per rifiutarla. Entrambi i metodi accettano un singolo che assegna il valore alla Promessa risultante.

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

Puoi anche utilizzare then() per gestire solo lo stato Completato e catch per per gestire lo stato rifiutato. Richiama catch con un singolo argomento contenente valore specificato nel metodo di rifiuto di 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."

A differenza di then e catch, che consentono l'esecuzione di una funzione gestore quando una promessa viene soddisfatta o rifiutata, una funzione passata come argomento a finally viene richiamato indipendentemente dal fatto che la promessa sia stata soddisfatta o rifiutata. La funzione gestore viene chiamata senza argomenti perché non è destinata a funzionano con i valori passati dalla Promise, solo per eseguire il codice dopo Promessa completata.

Contemporaneità

Il costruttore Promise offre quattro metodi per lavorare con più Promise, utilizzando un oggetto iterabile contenente oggetti Promise. Questi ogni metodo restituisce una Promessa, che viene soddisfatta o rifiutata in base allo stato delle Promesse che le sono passate. Promise.all(), ad esempio, crea una promessa che viene soddisfatta solo se ogni promessa passata a quel metodo viene soddisfatta:

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

I metodi di contemporaneità Promise sono i seguenti:

Promise.all()
Soddisfatto solo se tutte le promesse fornite sono soddisfatte.
Promise.any()
Soddisfatto se una delle promesse fornite viene rispettata e viene solo rifiutata se tutte le Promesse vengono rifiutate.
Promise.allSettled()
Soddisfatto quando le Promesse si sono risolte, indipendentemente dai risultati.
Promise.race()
Rifiutata o soddisfatta in base al risultato della prima promessa di saldatura. ignorando tutte le promesse saldate in seguito.

async/await

Quando utilizzi la parola chiave async prima di una dichiarazione di funzione o un'espressione di funzione, qualsiasi il valore restituito dalla funzione viene restituito come una promessa soddisfatta contenente valore. In questo modo puoi eseguire e gestire le operazioni asincrone utilizzando lo stesso i flussi di lavoro come sviluppo sincrono.

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

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

L'espressione await mette in pausa l'esecuzione di una funzione asincrona mentre la promessa associata viene saldata. Una volta risolta la Promessa, il valore l'espressione await è il valore della promessa, soddisfatto o rifiutato.

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

Qualsiasi valore non Promise incluso in un'espressione await viene restituito come Promessa compiuta:

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

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

Verifica le tue conoscenze

Che tipo di loop usi per ripetere l'iterazione su una quantità nota?

do...while
for
while