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.
Istruzioni condizionali
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.
if
…else
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."
switch
…case
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."
do
…while
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:
- Un'espressione da valutare all'inizio del ciclo
- Una condizione che determina se il loop deve continuare
- 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?
for
while
do...while