Flux de contrôle

Le flux de contrôle correspond à l'ordre dans lequel l'interpréteur JavaScript s'exécute. . Si un script n'inclut pas d'instructions qui modifient son flux, exécutées du début à la fin, une ligne à la fois. Les structures de contrôle sont utilisées pour déterminer si un ensemble d'instructions est exécuté ou non d'un ensemble défini de critères, d'exécuter un ensemble d'instructions de façon répétée ou d'interrompre une séquence d'instructions.

Instructions conditionnelles

Les instructions conditionnelles déterminent si le code doit être exécuté en fonction d'un ou d'autres conditions. Une instruction conditionnelle exécute le code qu'elle contient si le la condition (ou l'ensemble de conditions) associée prend la valeur true. Dans le cas contraire, est ignoré.

if... else

Une instruction if évalue une condition à l'intérieur des parenthèses correspondantes qui suivre. Si la condition entre parenthèses renvoie true, la ou relevé de blocage qui suit les parenthèses correspondantes est exécuté:

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

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

Si la condition entre parenthèses est évaluée à false, l'instruction qui suit, elle est ignorée:

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

Un mot clé else juste après une instruction if et ses exécutée de manière conditionnelle spécifie l'instruction à exécuter si la La condition if renvoie la valeur false:

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

Pour enchaîner plusieurs instructions if, vous pouvez définir Instruction exécutée de manière conditionnelle après else une autre instruction if:

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

Nous vous recommandons vivement d'utiliser la syntaxe d'instruction de bloc suivant les instructions conditionnelles pour améliorer la lisibilité, mais les clauses else if font souvent exception à cette règle:

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

Opérateur ternaire

if exécute une instruction de manière conditionnelle. L'opérateur ternaire (plus précisément, mais moins communément appelé opérateur conditionnel ternaire), est utilisé sous forme abrégée. pour exécuter une expression de manière conditionnelle. Comme son nom l'indique, est le seul opérateur JavaScript qui utilise trois opérandes:

  • Une condition à évaluer, suivie d'un point d'interrogation (?).
  • Expression à exécuter si la condition renvoie true, suivie d'une deux-points (:).
  • Expression à exécuter si la condition renvoie false.

Cette méthode est fréquemment utilisée pour définir ou transmettre une valeur de manière conditionnelle:

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

myFirstResult;
> "First value."

mySecondResult;
> "Second value."

switch... case

Utilisez l'instruction switch pour comparer la valeur d'une expression à une liste de les valeurs potentielles définies à l'aide d'un ou de plusieurs mots clés case. Cette syntaxe est inhabituelle car elle provient de certaines des premières décisions de conception de JavaScript. La syntaxe switch...case utilise le mot clé switch, suivi d'une expression pour être évaluées entre parenthèses, suivies d'une paire d'accolades identiques. Le corps de switch peut contenir case mots clés, généralement un ou plusieurs, suivie d'une expression ou d'une valeur, puis du signe deux-points (:).

Lorsque l'interpréteur atteint un case dont la valeur correspond à l'expression en cours évaluée entre parenthèses après le mot clé switch, il exécute toutes les qui suivent cette clause case:

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

Toutes les instructions qui suivent l'instruction case correspondante sont exécutées, même si elles sont incluse dans une instruction de bloc.

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

}
> "True."

L'un des pièges liés à l'utilisation de switch…case est qu'après la détection d'une correspondance, L'interpréteur JavaScript exécute n'importe quelle instruction qui suit l'instruction case correspondante, même celles incluses dans d'autres clauses case. C'est ce qu'on appelle la « tombée » vers case suivant:

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

Pour éviter toute interruption, terminez chaque cas par le mot clé break, qui arrête immédiatement l'évaluation du corps switch:

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

Si aucune case ne correspond à la valeur conditionnelle, switch sélectionne l'default s'il en existe une:

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

Toutefois, la chute s'applique également à default, ce qui peut entraîner des résultats inattendus. Pour résoudre ce problème, terminez votre instruction default par break. ou le placer en dernier dans la liste des cas.

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.

Étant donné que les clauses case ne nécessitent pas instruction de bloc pour le regroupement plusieurs instructions, les clauses case et default ne créent pas champ d'application lexical:

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

Pour gérer le champ d'application, utilisez des instructions de blocage:

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

Boucles et itérations

Les boucles vous permettent de répéter un ensemble d'instructions aussi longtemps qu'une condition est remplie. jusqu'à ce qu'une condition soit remplie. Utilisez des boucles pour exécuter un ensemble d'instructions un certain nombre de fois, jusqu'à l'obtention d'un résultat spécifique ou jusqu'à ce que l'interpréteur atteint la fin d'une structure de données itérable (par exemple, l'élément final dans un tableau, une carte ou un ensemble, la propriété finale d'un objet ou le dernier caractère de une chaîne).

Les boucles interrompent la lecture du haut vers le bas le flux d'exécution d'un script par itération sur un ensemble d'instructions, jusqu'à ce qu'une ou plusieurs conditions soient remplies ou ne soient plus est satisfaite, selon la syntaxe utilisée pour créer la boucle. Une fois la boucle terminée, l'exécution se poursuit avec les déclarations qui la suivent. Dans l'exemple suivant, les instructions du corps de la boucle sont exécutées trois fois avant que l'interprète passe à autre chose:

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

Si les conditions ne peuvent pas être remplies lors de l'exécution de la boucle, celle-ci se poursuit indéfiniment. Ces boucles infinies sont un piège de programmation courant qui peut le thread d'exécution principal de suspendre indéfiniment l'accès, voire de faire planter un onglet du navigateur.

L'exemple suivant s'exécute tant que la valeur booléenne true reste true Comme les valeurs booléennes sont immuables, cela crée une boucle infinie.

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

Évitez de laisser des boucles infinies dans votre code de production. Si vous créez accidentellement pendant le développement, vous pouvez le résoudre en fermant l'onglet de navigateur qu'il s'exécute en mettant à jour votre code de sorte que la boucle n'est plus infinie et en rouvrant .

while

Une boucle while est créée à l'aide du mot clé while suivi d'une paire de parenthèses correspondantes contenant une condition à évaluer. Si les valeurs prend initialement la valeur true, l'instruction (ou instruction en bloc) qui suit ces parenthèses est exécutée. Sinon, la boucle ne s'exécute jamais. Après chaque la condition est réévaluée. Si elle est toujours true, la boucle se répète.

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

Si l'interpréteur trouve une instruction continue dans une boucle while, il arrête cette l'itération, réévalue la condition et poursuit la boucle si possible:

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

Si l'interpréteur trouve une instruction break dans une boucle while, cette itération s'arrête et que la condition n'est pas réévaluée, ce qui permet à l'interprète de continuer:

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

Vous pouvez utiliser while pour itérer un nombre spécifié de fois, comme illustré dans exemple précédent, mais le cas d'utilisation le plus courant de while est une boucle de longueur indéterminée:

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 est une variante de la boucle while dans laquelle l'instruction conditionnelle l'évaluation a lieu à la fin de chaque itération de la boucle. Cela signifie que le corps de la boucle est toujours exécuté au moins une fois.

Pour créer une boucle do...while, utilisez le mot clé do suivi de l'instruction. (ou relevé de blocage) pour être à chaque itération de la boucle. Immédiatement après cette déclaration, ajoutez while et des parenthèses correspondantes contenant la condition à évaluer. Quand ? si cette condition ne renvoie plus la valeur true, la boucle se termine.

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

Comme pour une boucle while, le cas d'utilisation le plus courant pour do... while est une boucle de longueur indéterminée:

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

Utilisez des boucles for pour itérer sur une quantité connue. Dans les anciens codebases, il s'agissait fréquemment utilisé pour itérer les éléments d'un tableau.

Pour créer une boucle for, utilisez le mot clé for, suivi d'une paire de parenthèses. qui accepte les trois expressions suivantes dans l'ordre et séparées par Points-virgules:

  1. Expression à évaluer au début de la boucle
  2. Une condition qui détermine si la boucle doit se poursuivre
  3. Expression à exécuter à la fin de chaque boucle

Après ces parenthèses, ajoutez l’instruction (généralement une instruction de blocage). exécutées pendant la boucle.

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

La première expression initialise une variable qui agit comme un compteur. Ce est évaluée une fois, avant la première itération de la boucle. Vous pouvez initialisez cette variable en utilisant let (ou var, par le passé), comme n'importe quelle autre variable, et son champ d'application correspond au corps de la boucle. Ces variables peuvent avoir un identifiant valide, mais ils sont souvent appelés i pour "itération" ou "index". Cela semble contredire Bonnes pratiques concernant les noms d'identifiants prévisibles mais la convention est suffisamment bien établie pour être claire pour les autres développeurs de en un coup d'œil. Comme les collections indexées sont indexées à zéro, ces variables ont presque toujours une valeur initiale de 0.

Comme pour d'autres formes de boucle, la condition est une expression qui détermine si la boucle doit être exécutée. Elle sert le plus souvent à définir une valeur lié au compteur d'itérations. L'interpréteur évalue la condition avant exécute la boucle for pour la première fois.Si la condition n'est pas initialement prend la valeur true, le corps de la boucle n'est pas exécuté.

L'expression finale est exécutée à la fin de chaque itération dans la boucle. Il est généralement utilisé pour incrémenter l’identifiant d’une unité.

Le plus souvent, vous verrez des itérations for dans les tableaux dans des codebases. Dans ce cas, la condition spécifiée pour la poursuite de la boucle est une nombre d'itérations inférieur ou égal à la longueur du tableau itéré à travers. La variable utilisée pour suivre le nombre actuel d'itérations sert à rechercher la valeur associée à cet index dans le tableau, ce qui permet à chaque élément le tableau sur lequel agir dans l'ordre:

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

Cette approche a cessé d'être utilisée au profit d'approches plus modernes de une boucle dans des structures de données itérables.

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

Utilisez les boucles for...of... pour itérer les valeurs stockées dans une structure de données itérable, comme un tableau, un ensemble ou une carte.

Une boucle for...of... utilise le mot clé for, suivi d'une paire de parenthèses. contenant une variable, suivie de of, puis de la structure de données en cours d'itération. terminé. La variable peut être une déclaration effectuée ici à l'aide de let, const ou var, une variable déclarée précédemment dans le champ d'application actuel, un objet ou une instance de destructuration. Elle contient la valeur de l'élément qui correspond à l'itération actuelle. de la boucle.

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

Dans cet exemple, l'utilisation de const pour myElement fonctionne même si myElement est à une nouvelle valeur à chaque itération de la boucle. En effet, les variables déclarés avec let ou const sont limités à l'instruction de bloc dans la en boucle. La variable est initialisée au début de chaque itération et supprimée à la fin de cette itération.

for... in...

Utilisez les boucles for...in... pour itérer les propriétés énumérables d'un objet. y compris les propriétés héritées énumérables. Comme pour une boucle for...of..., une La boucle for...in... utilise le mot clé for, suivi d'une paire de parenthèses. contenant une variable contenant la valeur de la clé de propriété correspondant avec l'itération actuelle de la boucle. Cette variable est suivie du in, puis sur l'objet de l'itération:

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

Là encore, même si la valeur de myKey change à chaque itération de la boucle, vous pouvez utiliser const sans erreur, car la variable est effectivement supprimée. à la fin de chaque itération, puis recréés au début.

La valeur associée à chaque clé de propriété n'est pas directement disponible pour for...in... syntaxe. Cependant, comme la boucle a accès à une clé de propriété chaque itération, vous pouvez utiliser cette clé pour « rechercher » sa valeur:

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

Les propriétés héritées des constructeurs intégrés ne peuvent pas être énumérées, ce qui signifie que for...in... n'effectue pas d'itération des propriétés héritées de la Object. d'un constructeur. Cependant, toute propriété énumérable dans la classe chaîne de prototype sont inclus:

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 fournit des méthodes intégrées pour déterminer si une propriété est un propriété directe de l'objet plutôt qu'une propriété sur le prototype de l'objet chaîne: le modèle moderne Object.hasOwn() et les anciennes méthodes Object.prototype.hasOwnProperty(). Ces évaluent si une propriété spécifiée est héritée (ou non déclarée), en renvoyant true uniquement pour les propriétés immédiates d'un objet spécifié:

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"

Il existe également trois méthodes statiques qui renvoient chacune un tableau composé d'un Clés (Object.keys()), valeurs (Object.values()) de l'objet pouvant être énumérées ou des paires clé-valeur (Object.entries()):

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

Cela vous permet d'itérer des clés d'objet, des valeurs ou des paires clé-valeur (à l'aide de attribution de déstructuration). sans inclure les propriétés appartenant au prototype de cet objet:

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

Les méthodes forEach() fournies par le tableau Carte, Définir, et les constructeurs NodeList fournissent un raccourci utile pour effectuer des itérations sur des données dans le contexte d'une fonction de rappel. Contrairement aux autres formes de boucle, une boucle créée avec une méthode forEach() ne peut pas être interrompue à l'aide de break ou continue

forEach est une méthode appartenant au prototype de chaque structure de données. Chaque forEach attend une fonction de rappel en tant qu'argument, bien que celle-ci varie légèrement des arguments inclus lors de l'appel de cette fonction. Une seconde, facultatif spécifie une valeur this à utiliser comme contexte d'appel pour la de rappel.

La fonction de rappel utilisée avec Array.forEach fournit des paramètres contenant la valeur de l'élément actuel, son index et le tableau sur lequel la méthode forEach a été appelée:

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 fonction de rappel utilisée avec Map.forEach fournit des paramètres contenant le valeur associée à l'élément actuel, la clé associée à l'élément actuel et le mappage sur lequel la méthode forEach a été appelée:

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 rappel Set.forEach inclut des paramètres similaires. Comme Set ne comporte pas index ou clés distincts des valeurs, le deuxième argument fournit à la place une des valeurs redondantes et pouvant être ignorées, strictement pour assurer la cohérence de la syntaxe avec le d'autres méthodes 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 ]

Itérateurs

Un itérable est une structure de données constituée d'éléments individuels pouvant être en utilisant les approches détaillées précédemment. Un itérateur est un un objet itérable qui respecte le protocole d'itérateur, ce qui signifie qu'il doit implémenter Une méthode next() qui fait défiler les éléments qu'elle contient un par un, chaque fois que cette méthode est appelée, en renvoyant un objet pour chaque appel dans un format spécifique.

les structures de données itérables intégrées de JavaScript (telles que Array Map (Carte) Set) ne sont pas des itérateurs dans et de mais ils héritent tous d'une méthode iterator, accessible à l'aide de la méthode @@iterator symbole bien connu, qui renvoie un objet itérateur créé à partir de la structure de données itérable:

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

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

myIterator;
> Array Iterator {}

L'appel de la méthode next() sur un itérateur vous aide à parcourir les éléments qu'il contient un à la fois, chaque appel renvoyant un objet contenant deux : value, qui contient la valeur de l'élément actuel, et done, une valeur booléenne indiquant si l'itérateur a transmis le dernier élément dans la structure des données. La valeur de done n'est true que lorsqu'un appel à next() aboutit à une tentative d'accès à un élément au-delà du dernier élément du itérateur.

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 }

Fonctions de générateur

Utilisez le mot clé function* (notez l'astérisque) pour déclarer un générateur ou définissez une expression de fonction de générateur:

function* myGeneratorFunction() { };

Tout comme les itérateurs, les fonctions de générateur conservent l'état. Appel d'un de générateur renvoie un nouvel objet Generator, mais pas immédiatement exécuter le code dans le corps de la fonction:

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

myGeneratorObject;
> Generator {  }

typeof myGeneratorObject;
> "object"

Les objets de générateur respectent le protocole d'itérateur. La valeur attribuée à chaque appel Sur une fonction de générateur, next() est déterminé par une expression yield. qui met en pause l'exécution de la fonction de générateur et renvoie la valeur de la Expression contenant le mot clé yield. Appels ultérieurs au next() poursuivre l'exécution de la fonction en s'arrêtant à l'expression yield suivante ; renvoyant la valeur associée.

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 }

Lorsque next() est appelé après qu'aucune autre valeur n'est spécifiée à l'aide de yield, return ou throw (en cas d'erreur), le reste de la fonction s'exécute, et la valeur value de l'objet renvoyé est undefined et la valeur done de 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 }

N'utilisez next() que sur l'objet renvoyé par la fonction de générateur, et non sur le de la fonction de générateur elle-même. Sinon, chaque appel de la fonction de générateur crée un objet "generator" :

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

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

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

Comme pour toute fonction, la fonction de générateur s'arrête lorsqu'elle rencontre un return. mot clé. Il renvoie ensuite un objet au contexte d'appel contenant le a renvoyé une valeur, ainsi qu'une propriété done avec la valeur 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 }

Une expression yield peut utiliser une partie de la sémantique d'un identifiant, permettant une "communication" bidirectionnelle depuis et vers la partie suspendue du générateur. Lorsqu'une valeur est transmise à la méthode next() d'un générateur en tant que un argument, il remplace la valeur associée à l'argument "suspendu" Expression 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 }

Gardez à l'esprit que cette opération remplace l'intégralité de l'expression associée à yield précédente, et ne réattribue pas simplement la valeur de l'yield précédente à la valeur spécifiée dans 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 }

Tout argument transmis au premier appel à next() est ignoré, car il n'existe pas précédente expression yield pour accepter cette valeur. Comme pour toute autre fonction, les arguments transmis à l'appel initial de la fonction de générateur sont disponibles le champ d'application du corps de la fonction de générateur:

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'opérateur yield* (notez l'astérisque) est utilisé avec un itérable, tel que une autre fonction de générateur, pour itérer et donner à chaque valeur son opérande renvoie:

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 asynchrone

Bien que JavaScript soit fondamentalement synchrone, des mécanismes permettant aux développeurs de tirer parti la boucle d'événements pour effectuer des tâches asynchrones.

Promesses

Une promesse est un espace réservé pour une valeur inconnue lorsque la promesse est créé. Il s'agit d'un conteneur qui dicte une opération asynchrone, dont les termes que l'opération est considérée comme ayant réussi ou échoué, les actions à entreprendre dans les deux cas, et la valeur qui en résulte.

Créer une instance Promise à l'aide de l'opérateur new avec l'Promise intégré de la fonction constructeur. Ce constructeur accepte une fonction appelée exécuteur. comme argument. Cette fonction d'exécuteur est généralement utilisée pour exécuter une ou plusieurs des actions asynchrones, puis dictez les conditions dans lesquelles la promesse doit être considéré comme a été livré ou refusé. Une promesse est définie comme en attente pendant que la fonction d’exécuteur est en cours d’exécution. Une fois que l'exécuteur a terminé, une promesse est considéré comme réussi (ou résolu dans certaines sources) si la fonction d'exécuteur et l'action asynchrone qu'elle effectue sont terminées. correctement, et rejetée si la fonction de l'exécuteur rencontre une erreur ; ou l'action asynchrone en cours d'exécution échoue. Une fois la promesse réalisée ou refusée, elle est considérée comme réglée.

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

Le constructeur appelle la fonction d'exécuteur avec deux arguments. Ces arguments sont des fonctions qui vous permettent d'exécuter ou de refuser manuellement la promesse:

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

Les fonctions utilisées pour exécuter ou rejeter une promesse sont appelées avec le résultat valeur de la promesse en tant qu'argument (généralement une erreur en cas de refus):

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

Enchaînement de promesses

Vous pouvez agir sur l'objet Promise obtenu à l'aide des propriétés then(), catch() et Méthodes finally() héritées du constructeur Promise. Chacun de ces éléments renvoie une promesse qui peut être traitée immédiatement avec then(), catch() ou finally(), ce qui vous permet d'enchaîner les promesses obtenues.

then() fournit deux fonctions de rappel en tant qu'arguments. Utiliser la première pour répondre la promesse résultante et la seconde à la rejeter. Les deux méthodes n'acceptent qu'un seul qui donne sa valeur à la promesse obtenue.

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

Vous pouvez également utiliser then() pour ne gérer que l'état "réussi" et catch pour gérer l'état rejeté. Appelez catch avec un seul argument contenant le indiquée dans la méthode de rejet de la promesse:

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

Contrairement à then et catch, qui permettent à une fonction de gestionnaire de s'exécuter lorsqu'une promesse est traitée ou rejetée, une fonction transmise en tant qu'argument à finally est appelée, que la promesse ait été réalisée ou refusée. La fonction de gestionnaire est appelée sans argument, car elle n'est pas destinée travailler avec les valeurs transmises par la promesse, uniquement pour exécuter le code après la La promesse est remplie.

Simultanéité

Le constructeur Promise fournit quatre méthodes pour travailler avec plusieurs Promesses, à l'aide d'un élément iterable contenant des objets Promise Ces renvoient chacune une promesse qui est remplie ou rejetée en fonction de l'état des promesses qui lui sont transmises. Promise.all(), par exemple, crée une promesse qui n'est satisfaite que si toutes les promesses transmises à cette méthode sont remplies:

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

Les méthodes de simultanéité de Promise sont les suivantes:

Promise.all()
N'est traité que si toutes les promesses fournies sont remplies.
Promise.any()
Retirée si l'une des promesses fournies est remplie, mais seulement rejetée si toutes les promesses sont rejetées.
Promise.allSettled()
sont satisfaites lorsque les promesses ont été conclues, quel que soit leur résultat ;
Promise.race()
refus ou honorés en fonction du résultat de la première promesse de paiement ; en ignorant toutes les promesses établies ultérieurement.

async/await

Lorsque vous utilisez le mot clé async avant une déclaration de fonction ou expression de fonction, toute la valeur renvoyée par la fonction est renvoyée sous la forme d'une promesse remplie contenant cette . Cela vous permet d'exécuter et de gérer des opérations asynchrones à l'aide du même en tant que développement synchrone.

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

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

L'expression await met en pause l'exécution d'une fonction asynchrone tout en la promesse associée est réglée. Une fois la promesse établie, la valeur L'expression await correspond à la valeur honorée ou refusée de la promesse.

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

Toute valeur non liée à une promesse incluse dans une expression await est renvoyée sous forme de promesse remplie:

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

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

Testez vos connaissances

Quel type de boucle utilisez-vous pour itérer sur une quantité connue ?

for
while
do...while