Fluxo de controle

Fluxo de controle é a ordem em que o intérprete JavaScript é executado declarações. Se um script não inclui instruções que alteram seu fluxo, ele é executada do início ao fim, uma linha de cada vez. As estruturas de controle são usada para determinar se um conjunto de instruções é executado ou não com base em uma um conjunto de critérios definido, executar um conjunto de instruções repetidamente ou interromper uma sequência de instruções.

As instruções condicionais determinam se o código deve ser executado com base em mais condições. Uma instrução condicional executa o código que ela contém se o a condição associada (ou conjunto de condições) é avaliada como true. Caso contrário, o será ignorado.

if...else

Uma instrução if avalia uma condição dentro dos parênteses correspondentes que seguir. Se a condição dentro dos parênteses for avaliada como true, o Instrução ou block statement que segue os parênteses correspondentes é executado:

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

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

Se a condição entre parênteses for avaliada como false, a instrução que segue, ele será ignorado:

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

Uma palavra-chave else imediatamente após uma instrução if e a instrução executada condicionalmente, especifica a instrução a ser executada se o A condição if é avaliada como false:

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

Para encadear várias instruções if, faça o instrução condicionalmente executada após else outra instrução if:

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

Recomendamos o uso da sintaxe de instrução de bloco após condicionais para melhorar a legibilidade, mas as cláusulas else if geralmente são uma exceção a isso:

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 ternário

if executa uma instrução condicionalmente. O operador ternário (mais precisamente mas menos conhecida como operador condicional ternário) é uma abreviação usada para executar condicionalmente uma expressão. Como o nome indica, a função é o único operador JavaScript que usa três operandos:

  • Uma condição a ser avaliada, seguida por um ponto de interrogação (?).
  • A expressão a ser executada se a condição for avaliada como true, seguida por uma dois pontos (:).
  • A expressão a ser executada se a condição for avaliada como false.

É usado com frequência para definir ou transmitir condicionalmente um valor:

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

myFirstResult
;
> "First value."

mySecondResult
;
> "Second value."

switch...case

Use a instrução switch para comparar o valor de uma expressão a uma lista de valores potenciais definidos usando uma ou mais palavras-chave case. Essa sintaxe é incomuns, pois vêm de algumas das primeiras decisões de design do JavaScript. A sintaxe switch...case usa a palavra-chave switch, seguida por uma expressão para será avaliado entre parênteses, seguidos por um par correspondente de chaves. O corpo de switch pode conter palavras-chave case, geralmente uma ou mais, seguido de uma expressão ou valor e dois-pontos (:).

Quando o intérprete atinge um case com um valor correspondente à expressão que está sendo avaliado entre parênteses após a palavra-chave switch, ele executa qualquer instruções que seguem a cláusula case:

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

Todas as instruções após o case correspondente são executadas, mesmo se forem em uma instrução de bloco.

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

}
> "True."

Uma armadilha do uso de switch…case é que, depois que uma correspondência é encontrada, a O intérprete de JavaScript executa qualquer instrução que siga o case correspondente, mesmo as que estão dentro de outras cláusulas case. Isso é chamado de "cair", ao próximas case:

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

Para evitar falhas, encerre cada caso com a palavra-chave break, que interrompe imediatamente a avaliação do corpo switch:

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

Se nenhum case corresponder ao valor condicional, o switch selecionará o default. se houver:

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

No entanto, a queda também se aplica a default, possivelmente levando a resultados inesperados. Para corrigir isso, encerre sua instrução default com break, ou colocá-la por último na 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.

Como as cláusulas case não exigem uma instrução de bloco para agrupamento várias instruções, as cláusulas case e default não criam escopo léxico por si só:

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

Para gerenciar o escopo, use instruções de bloco:

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

Loops e iteração

As repetições permitem repetir um conjunto de instruções enquanto uma condição for atendida. Ou até que uma condição seja atendida. Usar repetições para executar um conjunto de instruções número de vezes, até que um resultado específico seja alcançado ou até que o intérprete chega ao final de uma estrutura de dados iterável (por exemplo, o elemento final em uma matriz, mapa ou conjunto, a propriedade final de um objeto ou o último caractere em uma string).

As repetições interrompem "de cima para baixo" de execução de um script iterando as sobre um conjunto de instruções até que uma ou mais condições sejam atendidas ou atendidos, dependendo da sintaxe usada para criar o loop. Após o término do loop, a execução continua para as instruções que a seguem. No exemplo a seguir, as instruções no corpo do loop são executadas três vezes antes da intérprete segue em frente:

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 as condições não puderem ser atendidas durante a execução da repetição, ela continuará indefinidamente. Essas loops infinitas são um problema comum de programação que pode fazer com que a linha de execução principal para pausar indefinidamente ou até mesmo travar uma guia do navegador.

O exemplo a seguir é executado enquanto o valor booleano true permanecer true: Como os valores booleanos são imutáveis, isso cria um loop infinito.

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

Evite deixar loops infinitos no código de produção. Se você criar, sem querer, durante o desenvolvimento, poderá corrigi-lo fechando a guia do navegador em execução atualizar seu código para que o loop não seja mais infinito e reabrir página.

while

Uma repetição while é criada usando a palavra-chave while seguida por um par de parênteses correspondentes que contêm uma condição a ser avaliada. Se o valor especificado inicialmente é avaliada como true, a instrução (ou block statement) que segue parênteses são executados. Caso contrário, a repetição nunca será executada. Após cada iteração, a condição será reavaliada e, se ainda for true, a repetição se repete.

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

Se o intérprete encontrar uma instrução continue em uma repetição while, ela vai interromper essa iteração, reavalia a condição e continua o loop, se possível:

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 o intérprete encontrar uma instrução break em uma repetição while, essa iteração é interrompida e a condição não é reavaliada, permitindo que o intérprete siga em frente:

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

É possível usar while para iterar um número especificado de vezes, como mostrado nas exemplo anterior, mas o caso de uso mais comum de while é uma repetição de comprimento indeterminado:

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 é uma variante da repetição while em que a condição A avaliação acontece no fim de cada iteração do loop. Isso significa que o corpo do loop é sempre executado pelo menos uma vez.

Para criar uma repetição do...while, use a palavra-chave do seguida pela instrução (ou declaração de bloqueio) executada em cada iteração da repetição. Imediatamente após essa instrução, adicione while e parênteses correspondentes que contêm a condição a ser avaliada. Quando essa condição não for mais avaliada como true, o loop será encerrado.

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

Como acontece com uma repetição while, o caso de uso mais comum para do...while é uma repetição de comprimento indeterminado:

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

Use repetições for para iterar até uma quantidade conhecida. Nas bases de código legadas, isso era usada com frequência para iterar os elementos de uma matriz.

Para criar uma repetição for, use a palavra-chave for, seguida por um conjunto de parênteses. que aceite as três expressões seguintes em ordem e separadas por Ponto e vírgula:

  1. Uma expressão a ser avaliada quando a repetição começa
  2. Condição que determina se o loop deve continuar.
  3. Uma expressão a ser executada na conclusão de cada loop.

Depois desses parênteses, adicione a instrução (normalmente uma block statement) sejam executada durante a repetição.

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

A primeira expressão inicializa uma variável que atua como contador. Isso é avaliada uma vez, antes da primeira iteração do loop. Você pode inicialize essa variável usando let (ou var, historicamente) como qualquer outro e o escopo dela é o corpo do loop. Essas variáveis podem ter identificador válido, mas muitas vezes são chamados de i para "iteração". ou "índice". Isso parece contradizer as decisões estabelecidas práticas recomendadas para nomes de identificadores previsíveis, mas a convenção está bem estabelecida o suficiente para ser clara para outros desenvolvedores da rapidamente. Como as coleções indexadas não são indexadas, essas variáveis quase sempre têm um valor inicial de 0.

Assim como em outras formas de repetição, a condição é uma expressão que determina se o loop precisa ser executado. Ele é mais usado para definir um valor vinculado ao contador de iterações. O intérprete avalia a condição antes ao executar a repetição for pela primeira vez.Se a condição não avaliar como true, o corpo da repetição não será executado.

A expressão final é executada ao final de cada iteração pelo loop. Geralmente, é usado para incrementar o identificador em um.

É mais comum encontrar repetições for iterando matrizes em matrizes mais antigas. bases de código. Nesses casos, a condição especificada para continuar o loop é uma contagem de iterações menor ou igual ao comprimento da matriz que está sendo iterada por A variável usada para rastrear a contagem de iteração atual é usada para procurar o valor associado a esse índice na matriz, permitindo que cada elemento em a matriz a ser executada na ordem:

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

Essa abordagem deixou de ser usada em favor de abordagens mais modernas para repetir estruturas de dados iteráveis.

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

Use repetições for...of... para iterar nos valores armazenados em uma estrutura de dados iterável, como uma matriz, conjunto ou mapa.

Uma repetição for...of... usa a palavra-chave for seguida por um conjunto de parênteses. contendo uma variável, seguida por of, e a estrutura de dados que está sendo iterada de novo. A variável pode ser uma declaração realizada aqui usando let, const ou var, uma variável declarada anteriormente no escopo atual, um objeto ou uma instância do desestruturar atribuição. Ele contém o valor do elemento que corresponde à iteração atual do loop.

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

Neste exemplo, o uso de const para myElement funciona mesmo que myElement seja recebe um novo valor em cada iteração do loop. Isso ocorre porque as variáveis declarados com let ou const têm o escopo da instrução de bloco dentro do repetição. A variável é inicializada no início de cada iteração e removida ao fim dessa iteração.

for...in...

Use repetições for...in... para iterar as propriedades enumeráveis de um objeto. incluindo propriedades herdadas enumeráveis. Assim como acontece com uma repetição for...of..., uma A repetição for...in... usa a palavra-chave for seguida por um conjunto de parênteses. que contém uma variável que contém o valor da chave de propriedade correspondente com a iteração atual do loop. Essa variável é seguida pelo Palavra-chave in, em seguida, o objeto que está sendo iterado:

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

Novamente, apesar do valor de myKey mudar a cada iteração da repetição, é possível usar const sem erro porque a variável é efetivamente descartada ao final de cada iteração e recriadas no início.

O valor associado a cada chave de propriedade não está diretamente disponível para o Sintaxe de for...in.... No entanto, como o loop tem acesso à chave de propriedade cada iteração, você pode usar essa chave para "procurar" seu valor:

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

As propriedades herdadas de construtores integrados não são enumeráveis, o que significa que for...in... não itera nas propriedades herdadas do Object construtor. No entanto, as propriedades enumeráveis dentro do protótipo cadeia estão incluídos:

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"

O JavaScript fornece métodos integrados para determinar se uma propriedade é uma propriedade direta do objeto em vez de uma propriedade no protótipo do objeto rede: o modelo moderno Object.hasOwn() e métodos Object.prototype.hasOwnProperty() legados. Esses avaliam se uma propriedade especificada é herdada (ou não declarada), retornando true apenas para as propriedades imediatas de um 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"

Há também três métodos estáticos que retornam uma matriz composta por uma Chaves enumeráveis (Object.keys()), valores (Object.values()) ou do objeto pares de chave-valor (Object.entries()):

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

Isso permite iterar chaves, valores ou pares de chave-valor de Objetos (usando o atribuição de desestruturação) sem incluir propriedades pertencentes ao protótipo desse 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()

Os métodos forEach() fornecidos pela Array, Mapa, Definir, e os construtores NodeList oferecem uma forma abreviada útil de iterar em uma linha no contexto de uma função de callback. Ao contrário de outras formas de loop, uma A repetição criada com qualquer método forEach() não pode ser interrompida usando break ou continue

forEach é um método do protótipo de cada estrutura de dados. Cada forEach método espera uma função de retorno de chamada como um argumento, embora eles variem um pouco em termos dos argumentos incluídos quando essa função é chamada. Um segundo, opcional especifica um valor this a ser usado como o contexto de invocação do função de callback.

A função de callback usada com Array.forEach fornece parâmetros que contêm o valor do elemento atual, o índice do elemento atual e a matriz em que o método forEach foi invocado:

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 ]

A função de callback usada com Map.forEach fornece parâmetros que contêm o valor associado ao elemento atual, a chave associada ao elemento atual e o mapa em que o método forEach foi invocado:

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 }

Um callback Set.forEach inclui parâmetros semelhantes. Como o Set não tem índices ou chaves diferentes dos valores, o segundo argumento fornece uma valor redundante e ignorável, estritamente para manter a sintaxe consistente com o outros 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

Um iterável é qualquer estrutura de dados composta por elementos individuais que podem ser iterado usando as abordagens detalhadas anteriormente. Um iterador é uma objeto iterável que segue o protocolo de iterador, o que significa que ele deve implementar um método next() que avança pelos elementos que contém, um de cada vez, sempre que esse método for chamado, retornando um objeto para cada ação elemento em um formato específico.

As estruturas de dados iteráveis integradas do JavaScript (como Matriz, Map e Set) não são iteradores em e de mas todos herdam um método iterator, que pode ser acessado usando o @@iterator Símbolo conhecido, que retorna um objeto iterador criado a partir da estrutura de dados iterável:

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

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

myIterator
;
> Array Iterator {}

Chamar o método next() em um iterador percorre os elementos que ele contém um por vez, com cada chamada retornando um objeto contendo duas propriedades: value, que contém o valor do elemento atual, e done, um booleano que informa se o iterador passou o último elemento em na estrutura de dados. O valor de done é true somente quando uma chamada para next() resulta em uma tentativa de acessar um elemento além do último na 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 }

Funções gerador

Use a palavra-chave function* (observe o asterisco) para declarar um gerador ou defina uma expressão de função geradora:

function* myGeneratorFunction() { };

Assim como os iteradores, as funções geradoras mantêm o estado. Chamar um função generator retorna um novo objeto Generator, mas não retorna imediatamente execute o código no corpo da função:

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

myGeneratorObject
;
> Generator {  }

typeof myGeneratorObject;
> "object"

Os objetos gerador seguem o protocolo de iterador. O valor de cada chamada O next() em uma função geradora retorna é determinado por uma expressão yield. que pausa a execução da função geradora e retorna o valor do expressão que contém a palavra-chave yield. Chamadas posteriores para next() continua a execução da função, pausando na próxima expressão yield e retornando o valor associado.

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() é chamado depois que nenhum outro valor é especificado usando yield, return ou throw (no caso de um erro), o restante da função O corpo é executado e o objeto retornado tem um value de undefined e um done propriedade 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 }

Use next() somente no objeto que a função geradora retornar, não no função geradora em si. Caso contrário, cada chamada à função gerador cria um novo objeto gerador:

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

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

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

Como acontece com qualquer função, a função do gerador é interrompida quando encontra um return. palavra-chave. Em seguida, ele retorna um objeto para o contexto de invocação que contém o valor retornado e uma propriedade done com o 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 }

Uma expressão yield pode assumir algumas das semânticas de um identificador, permitindo a "comunicação" bidirecional de e para a parte suspensa do função geradora. Quando um valor é transmitido para o método next() de um gerador como um argumento, ela substitui o valor associado ao prefixo anterior, Expressão 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 }

Lembre-se de que isso substitui toda a expressão associada ao yield anterior e não apenas reatribui o valor do yield anterior ao o valor especificado em 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 }

Qualquer argumento transmitido para a primeira chamada para next() é ignorado, porque não há expressão yield anterior para aceitar esse valor. Como em qualquer outra função, os argumentos passados para a chamada da função do gerador inicial estão disponíveis em todo escopo do corpo da função geradora:

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 }

O operador yield* (observe o asterisco) é usado com um iterável, como outra função de gerador, para iterar e gerar cada valor no operando dele retorna:

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

Embora o JavaScript seja fundamentalmente síncrono em execução, há mecanismos que permitem aos desenvolvedores aproveitar o loop de eventos para realizar tarefas assíncronas.

Promessas

Uma promessa é um marcador de posição para um valor que não é conhecido quando a promessa é criados. É um contêiner que dita uma operação assíncrona, os termos quais a operação é considerada um sucesso ou uma falha, as ações a serem tomadas em qualquer um dos casos e o valor resultante.

Criar uma instância de promessa usando o operador new com o Promise integrado função construtora. Esse construtor aceita uma função chamada executor. como argumento. Essa função de executor é normalmente usada para executar um ou mais ações assíncronas e, em seguida, ditar os termos pelos quais a promessa deve ser considerados concluídos ou rejeitados. Uma promessa é definida como pendente. enquanto a função do executor está em execução. Depois que o executor termina, uma promessa é considerada realizada (ou resolvida, em algumas fontes de documentação) se a função do executor e a ação assíncrona que ela executa são concluídas com sucesso e rejected se a função do executor encontrar um erro ou a ação assíncrona que está sendo realizada falhará. Depois que uma promessa for cumprida ou for rejeitada, ela será considerada resolvida.

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

O construtor chama a função do executor com dois argumentos. Esses argumentos são funções que permitem cumprir ou rejeitar manualmente a promessa:

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

As funções usadas para atender ou rejeitar uma promessa são chamadas com o resultado valor da promessa como um argumento (normalmente, um erro de rejeição):

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

Encadeamento de promessas

O objeto de promessa resultante pode ser usado usando os operadores then(), catch() e Métodos finally() herdados do construtor de promessas. Cada um desses retorna uma promessa, que pode ser executada imediatamente com then(). catch() ou finally() novamente, o que permite encadear as promessas resultantes.

then() fornece duas funções de callback como argumentos. Use o primeiro para atender a promessa resultante e o segundo a rejeitá-la. Os dois métodos aceitam um único que fornece o valor da promessa 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."

Também é possível usar then() para processar apenas o estado preenchido e catch para para processar o estado rejeitado. Chame catch com um único argumento contendo o valor fornecido no método de rejeição da promessa:

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

Ao contrário de then e catch, que permitem que uma função de gerenciador seja executada quando uma promessa for atendida ou rejeitada, uma função transmitida como um argumento para a finally é chamado independentemente de a promessa ter sido atendida ou rejeitada. A função de manipulador é chamada sem argumentos, porque ela não se destina a trabalham com os valores passados da promessa, somente para executar o código após a A promessa foi concluída.

Simultaneidade

O construtor de promessas fornece quatro métodos para trabalhar com vários métodos Promessas usando um iterable contendo objetos de promessa. Esses métodos retornam uma promessa, que é atendida ou rejeitada com base no estado. das promessas passadas a ela. Promise.all(), por exemplo, cria uma promessa que será cumprida somente se todas as promessas passadas a esse método forem cumpridas:

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

Os métodos de simultaneidade de promessas são os seguintes:

Promise.all()
Cumprida apenas se todas as promessas fornecidas forem cumpridas.
Promise.any()
Cumprido se qualquer uma das promessas fornecidas for cumprida, apenas recusada se todas as promessas forem recusadas.
Promise.allSettled()
Cumprida quando as promessas são resolvidas, independentemente do resultado.
Promise.race()
Rejeitada ou atendida com base no resultado da primeira promessa a ser resolvida, ignorando todas as promessas resolvidas depois.

async/await

Quando você usa a palavra-chave async antes de uma declaração de função ou expressão da função, qualquer valor retornado pela função é retornado como uma promessa cumprida que contém . Isso permite executar e gerenciar operações assíncronas usando a mesma fluxos de trabalho como desenvolvimento síncrono.

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

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

A expressão await pausa a execução de uma função assíncrona enquanto a promessa associada é resolvida. Depois que a promessa for resolvida, o valor a expressão await é o valor preenchido ou rejeitado da promessa.

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

Qualquer valor não promessa incluído em uma expressão await é retornado como uma Promessa cumprida:

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

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

Teste seu conhecimento

Que tipo de repetição você usa para iterar sobre uma quantidade conhecida?

do...while
while
for