Flujo de control

El flujo de control es el orden en que se ejecuta el intérprete de JavaScript. declaraciones. Si una secuencia de comandos no incluye sentencias que alteren su flujo, ejecutarse de principio a fin, una línea a la vez. Las estructuras de control son que se usa para determinar si un conjunto de sentencias se ejecuta o no en función de un de criterios definidos, ejecutar un conjunto de sentencias de forma repetida o interrumpir una secuencia de declaraciones.

Las instrucciones condicionales determinan si el código debe ejecutarse en función de uno o más condiciones. Una sentencia condicional ejecuta el código que contiene si el La condición asociada (o el conjunto de condiciones) se evalúa como true. De lo contrario, el el código fuente.

if... else

Una sentencia if evalúa una condición dentro de los paréntesis coincidentes que en las conversaciones que sigues. Si la condición dentro de los paréntesis se evalúa como true, la o la declaración de bloqueo que sigue los paréntesis coincidentes:

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

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

Si la condición dentro de los paréntesis se evalúa como false, la instrucción que que sigue, se ignora:

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

Una palabra clave else que aparece inmediatamente después de una sentencia if y su ejecutada condicionalmente especifica la sentencia que se ejecutará si el La condición if se evalúa como false:

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

Para encadenar varias sentencias if, puedes hacer lo siguiente: declaración ejecutada condicionalmente después de else otra declaración if:

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

Te recomendamos usar la sintaxis de la instrucción de bloqueo siguiendo los condicionales para mejoran la legibilidad, pero las cláusulas else if suelen ser una excepción a lo siguiente:

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

Operador ternario

if ejecuta una sentencia de forma condicional. El operador ternario (con mayor precisión pero con menos frecuencia se denomina operador condicional ternario) es la abreviatura. para ejecutar una expresión de forma condicional. Como su nombre lo indica, la ternaria es el único operador de JavaScript que usa tres operandos:

  • Una condición que se evaluará, seguida de un signo de interrogación (?).
  • La expresión que se ejecutará si la condición se evalúa como true, seguida de un dos puntos (:).
  • La expresión que se ejecutará si la condición se evalúa como false.

Con frecuencia, se usa para establecer o pasar un valor de forma condicional:

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

myFirstResult
;
> "First value."

mySecondResult
;
> "Second value."

switch... case

Usa la sentencia switch para comparar el valor de una expresión con una lista de valores potenciales definidos con una o más palabras clave case. Esta sintaxis es inusual, porque proviene de algunas de las primeras decisiones de diseño de JavaScript. switch... la sintaxis case usa la palabra clave switch, seguida de una expresión para Se evaluará entre paréntesis, seguido de un par de llaves. El cuerpo de switch puede contener case palabras clave, por lo general, una o más. seguido de una expresión o valor, seguido de dos puntos (:).

Cuando el intérprete alcanza un case con un valor que coincide con la expresión que se muestra. en el paréntesis después de la palabra clave switch, ejecuta cualquier sentencias que siguen a esa cláusula case:

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

Se ejecutan todas las sentencias posteriores a la case coincidente, incluso si encerrado en una declaración de bloqueo.

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

}
> "True."

Un error de usar switch…case es que, después de que se encuentra una coincidencia, el El intérprete de JavaScript ejecuta cualquier sentencia que siga al case coincidente. incluso aquellos dentro de otras cláusulas case. A esto se le llama "fallar" al case siguiente:

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

Para evitar la duplicación, finaliza cada caso con la palabra clave break, que Detiene de inmediato la evaluación del cuerpo switch:

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

Si no existe ningún elemento case que coincida con el valor condicional, switch selecciona default. si hay una:

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

Sin embargo, la entrada también se aplica a default, lo que podría generar resultados inesperados. Para corregir esto, termina la sentencia default con break, o colocarlo al final de la lista de casos.

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.

Debido a que las cláusulas case no requieren un declaración de bloqueo para la agrupación varias sentencias, las cláusulas case y default no crean alcance léxico por sí mismos:

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

Para administrar el alcance, usa sentencias de bloque:

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

Iteraciones y bucles

Los bucles te permiten repetir un conjunto de sentencias mientras se cumple una condición. hasta que se cumpla una condición. Usar bucles para ejecutar un conjunto de instrucciones que de veces, hasta que se logre un resultado específico o hasta que Llega al final de una estructura de datos iterable (por ejemplo, el elemento final de un array, un mapa o un conjunto; la propiedad final de un objeto, o el último carácter de una cadena).

Los bucles interrumpen la parte superior a la inferior. de la ejecución de una secuencia de comandos mediante la iteración sobre un conjunto de sentencias hasta que se cumplan una o más condiciones, o ya no se según la sintaxis que se use para crear el bucle. Cuando termina el bucle, ejecución continúa con las instrucciones que le siguen. En el siguiente ejemplo, Las sentencias en el cuerpo del bucle se ejecutan tres veces antes de que Este intérprete sigue:

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 no se pueden cumplir las condiciones durante la ejecución del bucle, este continúa indefinidamente. Estos bucles infinitos son un error de programación común que puede hace que el subproceso principal de ejecución se detengan de forma indefinida o bloqueen una pestaña del navegador.

El siguiente ejemplo se ejecutará mientras se mantenga el valor booleano true. true Debido a que los valores booleanos son inmutables, esto crea un bucle infinito.

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

Evita dejar bucles infinitos en tu código de producción. Si creas accidentalmente durante el desarrollo, puedes solucionarlo cerrando la pestaña del navegador en la que se ejecuta. actualizar tu código para que el bucle ya no sea infinito, y volver a abrir la .

while

Se crea un bucle while con la palabra clave while seguida de un par de paréntesis coincidentes que contienen una condición que se evaluará. Si el estado especificado condición se evalúa inicialmente como true, la instrucción (o instrucción de bloqueo) que le sigue se ejecutan esos paréntesis. De lo contrario, el bucle nunca se ejecutará. Después de cada iteración, se vuelve a evaluar la condición y, si sigue siendo true, el bucle se repita.

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

Si el intérprete encuentra una sentencia continue en un bucle while, la detiene. e iteración, reevalúa la condición y continúa el bucle, si es posible:

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 el intérprete encuentra una sentencia break en un bucle while, esa iteración se detiene y la condición no se reevalúa, lo que permite que el intérprete continúe con lo siguiente:

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

Puedes usar while para iterar una cantidad específica de veces, como se ve en la ejemplo anterior, pero el caso de uso más común para while es un bucle de longitud indeterminada:

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 es una variante del bucle while en la que la variable condicional La evaluación ocurre al final de cada iteración del bucle. Esto significa que del bucle se ejecuta siempre al menos una vez.

Para crear un bucle do...while, usa la palabra clave do seguida de la sentencia. (o instrucción de bloqueo) que se ejecutará en cada iteración del bucle. Inmediatamente después de esa declaración, agrega while y paréntesis coincidentes que contienen la condición que se evaluará. Cuándo esta condición ya no se evalúa como true, el bucle finaliza.

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

Al igual que con un bucle while, el caso de uso más común para do...while es un bucle de longitud indeterminada:

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

Usa bucles for para iterar en una cantidad conocida. En las bases de código heredadas, esta era que suele usarse para iterar sobre los elementos de un array.

Para crear un bucle for, usa la palabra clave for seguida de un conjunto de paréntesis. que acepte las siguientes tres expresiones en orden y separadas por punto y coma:

  1. Una expresión que se evaluará cuando comience el bucle
  2. Una condición que determina si el bucle debe continuar
  3. Una expresión que se ejecutará al final de cada bucle

Después de estos paréntesis, agrega la instrucción (por lo general, un instrucción de bloqueo) que se ejecute durante el bucle.

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

La primera expresión inicializa una variable que actúa como un contador. Esta se evalúa una vez, antes de la primera iteración del bucle. Puedes inicializa esta variable usando let (o var, históricamente) como cualquier otro variable y su alcance es el cuerpo del bucle. Estas variables pueden tener cualquier identificador válido, pero con frecuencia se los llama i para “iteración”. o "índice". Esto parece contradecir lo establecido prácticas recomendadas para nombres de identificadores predecibles, pero la convención está lo suficientemente bien establecida como para ser clara para otros desarrolladores en de un vistazo. Debido a que las colecciones indexadas no están indexadas, estas variables casi siempre tienen un valor inicial de 0.

Al igual que ocurre con otras formas de bucle, la condición es una expresión que determina si el bucle debe ejecutarse. Se usa con mayor frecuencia para establecer limitado para el contador de iteraciones. El intérprete evalúa la condición antes ejecutar el bucle for por primera vez.Si la condición no evalúa como true, el cuerpo del bucle no se ejecuta.

La expresión final se ejecuta al final de cada iteración a través del bucle. Por lo general, se usa para aumentar el identificador en uno.

Con más frecuencia, verás bucles for iterando a través de arrays en versiones anteriores. de código abierto. En estos casos, la condición especificada para continuar el bucle es una recuento de iteraciones menor o igual que la longitud de la matriz que se itera a través de él. La variable que se usa para rastrear el recuento de iteraciones actual se usa para buscar el valor asociado con ese índice en el array, lo que permite que cada elemento de el array sobre el que se debe actuar en orden:

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

Este enfoque ha dejado de usarse y se lo reemplazó por enfoques más modernos para bucles a través de estructuras de datos iterables.

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

Usa los bucles for...of... para iterar sobre los valores almacenados en un estructura de datos iterables, como un array, un conjunto o un mapa.

Un bucle for...of... usa la palabra clave for seguida de un conjunto de paréntesis. que contiene una variable, seguida de of y, luego, la estructura de datos que se itera de nuevo. La variable puede ser una declaración realizada aquí con let, const, o var, una variable declarada anteriormente dentro del alcance actual, un objeto propiedad o una instancia de desestructuración de la tarea. Contiene el valor del elemento que corresponde a la iteración actual del bucle.

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

En este ejemplo, el uso de const para myElement funciona aunque myElement sea se les da un nuevo valor en cada iteración del bucle. Esto se debe a que las variables declaradas con let o const tienen alcance en la sentencia de bloqueo dentro del bucle. La variable se inicializa al comienzo de cada iteración y se quita en el final de esa iteración.

for... in...

Usa los bucles for...in... para iterar sobre las propiedades enumerables de un objeto. incluidas las propiedades heredadas que se pueden enumerar. Al igual que con un bucle for...of..., un El bucle for...in... usa la palabra clave for seguida de un conjunto de paréntesis con una variable que contenga el valor de la clave de propiedad correspondiente con la iteración actual del bucle. A esta variable le sigue el in y, luego, el objeto sobre el que se itera:

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

Nuevamente, a pesar de que el valor de myKey cambia con cada iteración del bucle, puedes usar const sin errores porque la variable se descarta eficazmente. al final de cada iteración y, luego, se vuelven a crear al inicio.

El valor asociado con cada clave de propiedad no está disponible directamente para Sintaxis de for...in.... Sin embargo, como el bucle tiene acceso a la clave de propiedad en de cada iteración, puedes usar esa clave para "buscar" su valor:

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

Las propiedades heredadas de constructores integrados no son enumerables, lo que significa que for...in... no itera a través de las propiedades heredadas de Object. . Sin embargo, cualquier propiedad enumerable dentro del nombre cadena de prototipos:

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 proporciona métodos integrados para determinar si una propiedad es una propiedad directa del objeto en lugar de una propiedad del prototipo del objeto cadena: la estructura moderna Object.hasOwn() y los métodos Object.prototype.hasOwnProperty() heredados Estos evalúan si una propiedad especificada es heredada (o no declarada) Se muestra true solo para las propiedades inmediatas de un objeto especificado:

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"

También hay tres métodos estáticos que muestran un array compuesto por un elemento Claves enumerables del objeto (Object.keys()), valores (Object.values()) o Pares clave-valor (Object.entries()):

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

Esto te permite iterar claves, valores o pares clave-valor de objetos (con desestructuración de tareas). sin incluir propiedades que sean propiedad del prototipo de ese objeto:

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

Los métodos forEach() que proporciona Array Mapa, Establecer, y los constructores de NodeList proporcionan una abreviatura útil para iterar sobre un conjunto de datos. en el contexto de una función de devolución de llamada. A diferencia de otras formas de bucle, un el bucle creado con cualquier método forEach() no se puede interrumpir mediante break continue

forEach es un método que pertenece al prototipo de cada estructura de datos. Cada forEach de destino espera una función de devolución de llamada como argumento, aunque varían ligeramente entre términos de los argumentos incluidos cuando se llama a esa función. Un segundo (opcional) especifica un valor this para usar como contexto de invocación del función de devolución de llamada.

La función de devolución de llamada que se usa con Array.forEach proporciona parámetros que contienen El valor del elemento actual, el índice del elemento actual y el array en el que se invocó el método 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 función de devolución de llamada que se usa con Map.forEach proporciona parámetros que contienen el valor asociado con el elemento actual, la clave asociada con el elemento y el Map en el que se invocó el método forEach:

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 }

Una devolución de llamada Set.forEach incluye parámetros similares. Como el conjunto no tiene o claves distintas de los valores, el segundo argumento proporciona una es redundante, es un valor que se puede ignorar, para que la sintaxis sea coherente otros métodos 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 ]

Iteradores

Un iterable es cualquier estructura de datos compuesta por elementos individuales que se pueden e iterarse con respecto a los enfoques detallados anteriormente. Un iterador es un objeto iterable que sigue el protocolo iterador, lo que significa que debe implementar Un método next() que avanza por los elementos que contiene uno a la vez cada vez que se llama a ese método, de modo que se devuelve un objeto por cada en un formato específico.

Las estructuras de datos iterables integradas de JavaScript (como Array, Mapa y Set) no son iteradores en y de pero todos heredan un método iterator, al que se puede acceder con el @@iterator símbolo conocido, que muestra un objeto iterador creado a partir de la estructura de datos iterable:

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

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

myIterator
;
> Array Iterator {}

Llamar al método next() en un iterador recorre los elementos que contiene contiene uno a la vez, y cada llamada muestra un objeto que contiene dos propiedades: value, que contiene el valor del elemento actual. done, un valor booleano que indica si el iterador pasó el último elemento en la estructura de los datos. El valor de done es true solo cuando una llamada a next(). provoca un intento de acceso a un elemento más allá del último elemento de la iterador.

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 }

Funciones del generador

Usa la palabra clave function* (ten en cuenta el asterisco) para declarar un generador función o definir una expresión de función del generador:

function* myGeneratorFunction() { };

Al igual que los iteradores, las funciones del generador mantienen el estado. Llamar a un generator muestra un nuevo objeto Generator, pero no ejecuta el código en el cuerpo de la función:

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

myGeneratorObject
;
> Generator {  }

typeof myGeneratorObject;
> "object"

Los objetos generadores siguen el protocolo del iterador. El valor que cada llamada a next() en una función de generador que muestra está determinado por una expresión yield. lo que pausa la ejecución de la función del generador y devuelve el valor del expresión que contiene la palabra clave yield. Llamadas posteriores a next() continúa la ejecución de la función, pausando en la siguiente expresión yield y para mostrar el valor asociado.

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 }

Cuando se llama a next() después de que no se especifican más valores mediante yield, return o throw (en caso de un error); el resto de la función se ejecuta, y el objeto que se devuelve tiene un value de undefined y un done propiedad 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 }

Usa next() solo en el objeto que muestra la función del generador, no en el generador de salida. De lo contrario, cada llamada a la función del generador crea un nuevo objeto generador:

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

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

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

Al igual que con cualquier función, la función de generador se detiene cuando encuentra una return. palabra clave. Luego, devuelve un objeto al contexto de invocación que contiene el el valor que se muestra y una propiedad done con el valor 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 }

Una expresión yield puede adoptar parte de la semántica de un identificador. permitir la "comunicación" bidireccional desde la parte suspendida de la generator. Cuando se pasa un valor al método next() de un generador como un argumento, reemplaza el valor asociado con el estado Expresión 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 }

Ten en cuenta que esto reemplaza toda la expresión asociada con el yield anterior y no reasigna solo el valor del yield anterior a el valor especificado en 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 }

Se ignora cualquier argumento que se pase a la primera llamada a next(), ya que no hay la expresión yield anterior para aceptar ese valor. Al igual que con cualquier otra función, los argumentos que se pasan a la llamada inicial a función del generador están disponibles en el alcance del cuerpo de la función de generador:

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 }

El operador yield* (ten en cuenta el asterisco) se usa con un elemento iterable, como otra función generadora, para iterar y proporcionar cada valor a su operando muestra lo siguiente:

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 asíncrono

Aunque JavaScript es básicamente síncrono, en ejecución, existen mecanismos que les permiten a los desarrolladores aprovechar el bucle de eventos para realizar las tareas asíncronas.

Promesas

Una promesa es un marcador de posición para un valor que no se conoce cuando la promesa se crear. Es un contenedor que dicta una operación asíncrona. Los términos en el que la operación se considera exitosa o fracaso, las medidas que se deben tomar en cualquier caso y el valor que se genera.

Crea una instancia de Promesa usando el operador new con el Promise integrado. función de constructor. Este constructor acepta una función llamada executor. como argumento. Esa función de ejecutor generalmente se usa para realizar una o más acciones asíncronas y, luego, dictar los términos según los cuales se debe se consideró completada correctamente o rechazada. Una promesa se define como pendiente. mientras se ejecuta la función ejecutora. Después de que el ejecutor finaliza, aparece una promesa se considera completado (o resuelto, en algunas fuentes de documentación) si se completan la función de ejecutor y la acción asíncrona que realiza con éxito y rechazada si la función ejecutora encuentra un error, o la acción asíncrona que se realiza falla. Después de que se cumple una promesa o rechazado, se considera resuelta.

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

El constructor llama a la función de ejecutor con dos argumentos. Esos argumentos son funciones que te permiten cumplir o rechazar manualmente la promesa:

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

Las funciones que se usan para cumplir o rechazar una promesa se llaman valor de la promesa como argumento (generalmente, un error por rechazo):

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

Encadenamiento de promesas

Se puede actuar sobre el objeto Promise resultante mediante los métodos then(), catch() y Métodos finally() heredados del constructor de Promise. Cada uno de estos muestra una promesa, en la que se puede actuar de inmediato con then(), catch() o finally() de nuevo, lo que te permite encadenar las promesas resultantes.

then() proporciona dos funciones de devolución de llamada como argumentos. Usa la primera para completar la promesa resultante, y el segundo en rechazarla. Ambos métodos aceptan un solo que da su valor a la promesa resultante.

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

También puedes usar then() para controlar solo el estado completado, y catch para manejar el estado rechazado. Llama a catch con un solo argumento que contenga lo siguiente: proporcionado en el método de rechazo de la promesa:

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 diferencia de then y catch, que permiten que se ejecute una función de controlador cuando una promesa se completa o se rechaza, una función pasada como argumento al finally independientemente de si la promesa se cumplió o se rechazó. Se llama a la función del controlador sin argumentos, ya que no está diseñada para trabajar con los valores que se pasaron de la promesa, solo para ejecutar el código después de La promesa se completó.

Simultaneidad

El constructor de Promise proporciona cuatro métodos para trabajar con varios métodos promesas, con un objeto iterable que contenga objetos Promise. Estos cada método devuelve una promesa, que se cumple o rechaza según el estado. de las promesas que se le pasaron. Por ejemplo, Promise.all() crea una promesa. que se cumpla solo si se cumple cada promesa pasada a ese método:

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

Los métodos de simultaneidad de las promesas son los siguientes:

Promise.all()
Se entrega solo si se cumplen todas las promesas proporcionadas.
Promise.any()
Se cumple si se cumple alguna de las promesas proporcionadas y solo se rechaza. si se rechazan todas las promesas.
Promise.allSettled()
Se cumplen cuando se establecen las promesas, sin importar el resultado.
Promise.race()
Se rechaza o se cumple en función del resultado de la primera promesa de liquidación, ignorando todas las promesas que se cumplieron más adelante.

async/await

Cuando usas la palabra clave async antes de una declaración de función. o expresión de función, cualquier valor que muestra la función se devuelve como una promesa cumplida que contiene ese valor. Esto te permite ejecutar y administrar operaciones asíncronas con el mismo como desarrollo síncrono.

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

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

La expresión await pausa la ejecución de una función asíncrona mientras se establece la promesa asociada. Después de que se establece la promesa, el valor de La expresión await es el valor cumplido o rechazado de la promesa.

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

Cualquier valor que no sea de promesa incluido en una expresión await se muestra como un Promesa cumplida:

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

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

Verifica tus conocimientos

¿Qué tipo de bucle usas para iterar en una cantidad conocida?

while
for
do...while