制御フロー

制御フローとは、JavaScript インタープリタがステートメントを実行する順序です。フローを変更するステートメントがスクリプトに含まれていない場合は、最初から最後まで、1 行ずつ実行されます。制御構造は、定義された条件のセットに基づいて一連のステートメントが実行されるかどうか、一連のステートメントを繰り返し実行するかどうか、一連のステートメントが中断されるかどうかを判断するために使用されます。

条件文は、1 つ以上の条件に基づいてコードを実行するかどうかを決定します。条件文は、関連する条件(または条件セット)が true と評価された場合、含まれているコードを実行します。それ以外の場合、コードはスキップされます。

if ステートメントは、その後に続くかっこ内の条件を評価します。かっこ内の条件が true と評価されると、一致したかっこに続くステートメントまたはブロック ステートメントが実行されます。

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

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

かっこ内の条件が false と評価された場合、その後のステートメントは無視されます。

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

if ステートメントとその条件付き実行ステートメントの直後の else キーワードは、if 条件が false と評価された場合に実行されるステートメントを指定します。

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

複数の if ステートメントを連結するには、else の後に別の if ステートメントの後に条件付き実行ステートメントを作成します。

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

読みやすくするために、条件の後にブロック ステートメントの構文を使用することを強くおすすめしますが、多くの場合、else if 句は例外です。

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

if は、ステートメントを条件付きで実行します。3 項演算子(より正確には、3 項条件演算子とは呼ばれませんが、より正確には)は、条件付きで式を実行するために使用する省略形です。その名が示すように、3 項演算子は、次の 3 つのオペランドを使用する唯一の JavaScript 演算子です。

  • 評価する条件の後に疑問符(?)が続きます。
  • 条件が true と評価された場合に実行される式。その後にコロン(:)が続きます。
  • 条件が false と評価された場合に実行される式。

これは、条件付きで値を設定または渡すためによく使用されます。

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

myFirstResult
;
> "First value."

mySecondResult
;
> "Second value."

switch ステートメントは、式の値を、1 つ以上の case キーワードを使用して定義された潜在的な値のリストと比較します。この構文は、JavaScript の初期の設計上の決定に起因しているため、一般的ではありません。switch...case 構文では、switch キーワードの後に、評価対象の式を括弧で囲み、その後に中括弧の一致ペアを続けます。switch の本文には、case キーワード(通常は 1 つ以上)を記述し、その後に式または値、コロン(:)を続けます。

インタープリタは、switch キーワードの後のかっこ内にある評価対象の式と一致する値を持つ case に到達すると、その case 句に続くすべてのステートメントを実行します。

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

一致する case に続くすべてのステートメントは、ブロック ステートメント内に囲まれている場合でも実行されます。

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

}
> "True."

switch…case を使用する場合の注意点として、一致が見つかった場合、JavaScript インタープリタは一致した case に続くすべてのステートメント(他の case 句内のステートメントを含む)を実行することがあります。これを次の case に「フォールスルー」と呼びます。

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

フォールバックを防ぐには、各ケースを break キーワードで終了します。これにより、switch 本文の評価が直ちに停止します。

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

条件値に一致する case がない場合、switchdefault 句を選択します(存在する場合)。

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

ただし、フォールバックは default にも適用されるため、予期しない結果が生じる可能性があります。この問題を解決するには、default ステートメントを break で終わるか、ケースのリストの最後に配置します。

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.

case 句は複数のステートメントをグループ化するためのブロック ステートメントを必要としないため、case 句と default 句はそれ自体で語彙スコープを作成しません。

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

スコープを管理するには、ブロック ステートメントを使用します。

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

ループと反復処理

ループを使用すると、条件が満たされている間、または条件が満たされるまで、一連のステートメントを繰り返すことができます。ループを使用して、特定の結果が得られるまで、またはインタープリタが反復可能なデータ構造の終わり(配列、マップ、セットの最後の要素、オブジェクトの最後のプロパティ、文字列の最後の文字など)に達するまで、一連の命令を一定回数実行します。

ループは、ループの作成に使用される構文に応じて、1 つ以上の条件が満たされるか、満たされなくなるまで、一連のステートメントを反復処理することでスクリプトの実行の「上から下」のフローを中断します。ループが終了すると、後続のステートメントの実行が続行されます。次の例では、インタープリタが先に進む前に、ループの本文のステートメントが 3 回実行されます。

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

ループの実行中に条件が満たされない場合、ループは無期限に継続します。このような無限ループは、プログラミングでよくある問題です。メイン実行スレッドが無期限に一時停止したり、ブラウザタブをクラッシュさせたりすることがあります。

次の例では、ブール値 truetrue である限り実行されます。ブール値は不変であるため、無限ループが発生します。

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

本番環境のコードに無限ループが残らないようにします。開発中に誤って作成した場合は、実行中のブラウザタブを閉じて、ループが無限にならなくなるようにコードを更新してから、ページを再度開くことで修正できます。

while

while ループを作成するには、while キーワードの後に、評価される条件を含む一致したかっこのペアを指定します。指定された条件が最初に true と評価されると、これらのかっこの後に続くステートメント(またはブロック ステートメント)が実行されます。そうでない場合、ループは実行されません。反復処理のたびに条件が再評価され、それでも true であればループが繰り返されます。

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

インタープリタは、while ループ内で continue ステートメントを見つけると、その反復処理を停止し、条件を再評価して、可能であればループを続行します。

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

インタープリタが while ループ内で break ステートメントを検出すると、その反復処理は停止し、条件は再評価されないため、インタープリタは処理を続行します。

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

上記の例に示すように、while を使用して指定された回数を反復処理できますが、while の最も一般的なユースケースは不定の長さのループです。

let randomize = () => Math.floor( Math.random() \* 10 );
let randomNum
= randomize();
while( randomNum !== 3 ){
  console
.log( `The number is not ${ randomNum }.` );
  randomNum
= randomize();
}
console
.log( `The correct number, ${ randomNum }, was found.` );
> "The number is not 0."
> "The number is not 6."
> "The number is not 1."
> "The number is not 8."
> "The correct number, 3, was found."

dowhile

do...whilewhile ループのバリアントであり、条件評価はループの各反復処理の終わりで行われます。つまり、ループの本文は常に少なくとも 1 回実行されます。

do...while ループを作成するには、do キーワードの後に、ループの反復処理ごとにステートメント(またはブロック ステートメント)を実行します。そのステートメントの直後に while を追加し、評価する条件を含む対応するかっこを追加します。この条件が true と評価されなくなると、ループが終了します。

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

while ループと同様に、do...while の最も一般的なユースケースは、長さが不確定なループです。

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

for ループを使用して、既知の数量を反復処理します。以前のコードベースでは、配列内の要素を反復処理するために頻繁に使用されていました。

for ループを作成するには、for キーワードの後に、次の 3 つの式をセミコロンで区切ったかっこで囲みます。

  1. ループの開始時に評価される式
  2. ループを続行するかどうかを決定する条件
  3. 各ループの終了時に実行される式

これらのかっこの後に、ループ中に実行するステートメント(通常はブロック ステートメント)を追加します。

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

最初の式は、カウンタとして機能する変数を初期化します。この式は、ループの最初の反復処理の前に 1 回評価されます。この変数は、他の変数と同様に let(従来は var)を使用して初期化でき、スコープはループの本体です。これらの変数には任意の有効な識別子を指定できますが、「反復」または「インデックス」のために i と呼ばれることがよくあります。これは、確立された予測可能な識別子名のベスト プラクティスと矛盾するように見えますが、その慣例は確立されているため、他のデベロッパーが一目で理解できます。インデックス付きコレクションにはゼロのインデックスが作成されるため、これらの変数の初期値はほぼ常に 0 になります。

他の形式のループと同様に、条件はループを実行するかどうかを決定する式です。これは、ほとんどの場合、反復カウンタの上限を設定するために使用されます。インタープリタは、for ループを初めて実行する前に条件を評価します。条件が最初に true と評価されない場合、ループの本文は実行されません。

最後の式は、ループの各反復処理の最後に実行されます。これは通常、識別子を 1 つ増やすために使用されます。

古いコードベースでは、配列を反復処理する for ループが最も一般的です。この場合、ループの続行に指定する条件は、反復処理対象の配列の長さ以下の反復回数です。現在の反復回数のトラッキングに使用される変数は、配列内のそのインデックスに関連付けられた値を検索するために使用されます。これにより、配列内の各要素を順番に処理できるようになります。

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

このアプローチは、反復可能なデータ構造をループさせる最新のアプローチに置き換えられ、使用されなくなりました。

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

for...of... ループを使用して、配列、セット、マップなど、反復可能なデータ構造に格納されている値を反復処理します。

for...of... ループでは、for キーワードの後に変数を含むかっこ、of、反復処理対象のデータ構造と続きます。この変数は、letconst、または var、現在のスコープ内で以前に宣言された変数、オブジェクト プロパティ、または分解割り当てのインスタンスを使用して、ここで実行される宣言です。これには、ループの現在の反復処理に対応する要素の値が含まれます。

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

この例では、ループの反復処理ごとに myElement に新しい値が渡される場合でも、myElementconst を使用できます。これは、let または const で宣言された変数のスコープが、ループ内のブロック ステートメントであるためです。この変数は、各反復処理の開始時に初期化され、その反復処理の終了時に削除されます。

forin

for...in... ループを使用して、オブジェクトの列挙型プロパティ(継承可能な列挙型プロパティを含む)を反復処理します。for...of... ループと同様に、for...in... ループでは、for キーワードの後に、ループの現在の反復処理に対応するプロパティキーの値を含む変数を含む括弧のペアが続きます。この変数の後に in キーワード、そして反復対象のオブジェクトが続きます。

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

ここでも、myKey の値はループの反復処理ごとに変化しますが、変数は各反復処理の終了時に実質的に破棄され、開始時に再作成されるため、エラーなしで const を使用できます。

各プロパティキーに関連付けられた値を for...in... 構文で直接使用することはできません。ただし、ループは反復処理ごとにプロパティキーにアクセスできるため、そのキーを使用して値を「検索」できます。

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

組み込みコンストラクタから継承されたプロパティは列挙できません。つまり、for...in... は、Object コンストラクタから継承されたプロパティを反復処理しません。ただし、オブジェクトのプロトタイプ チェーン内の列挙可能なプロパティは含まれます。

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 には、プロパティがオブジェクトのプロトタイプ チェーン上のプロパティではなく、オブジェクトの直接プロパティであるかどうかを判別するための組み込みメソッド(最新の Object.hasOwn() メソッドと従来の Object.prototype.hasOwnProperty() メソッド)が用意されています。これらのメソッドは、指定されたプロパティが継承されている(または宣言されていない)かどうかを評価し、指定されたオブジェクトの即時プロパティについてのみ true を返します。

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"

また、オブジェクトの列挙キー(Object.keys())、値(Object.values())、Key-Value ペア(Object.entries())で構成される配列を返す 3 つの静的メソッドもあります。

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

これにより、そのオブジェクトのプロトタイプが所有するプロパティを含めずに、オブジェクトのキー、値、または Key-Value ペアを(分離割り当てを使用して)反復処理できます。

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

ArrayMapSet、NodeList のコンストラクタにより提供される forEach() メソッドは、コールバック関数のコンテキストでデータ構造体に対して反復処理を行う際に役立つ簡略記法です。他の形式のループとは異なり、forEach() メソッドで作成されたループは、break または continue を使用して中断できません。

forEach は、各データ構造のプロトタイプが所有するメソッドです。各 forEach メソッドはコールバック関数を引数として想定していますが、その関数の呼び出し時に含まれる引数に関しては、その関数が若干異なります。2 番目のオプションの引数では、コールバック関数の呼び出しコンテキストとして使用する this 値を指定します。

Array.forEach で使用されるコールバック関数は、現在の要素の値、現在の要素のインデックス、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 ]

Map.forEach で使用されるコールバック関数は、現在の要素に関連付けられた値、現在の要素に関連付けられたキー、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 }

Set.forEach コールバックには同様のパラメータが含まれています。Set には値と区別できるインデックスやキーがないため、2 番目の引数には、他の 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 ]

イテレータ

iterable は、前述のアプローチで反復処理できる個々の要素で構成されるデータ構造です。イテレータは、イテレータ プロトコルに従うイテラブル オブジェクトです。つまり、このメソッドが呼び出されるたびに、含まれる要素を一度に 1 つずつ進めて、特定の形式でシーケンシャルな要素ごとにオブジェクトを返す next() メソッドを実装する必要があります。

JavaScript の組み込みイテラブル データ構造(ArrayMapSet など)は、それ自体がイテレータではありませんが、すべて iterator メソッドを継承します。このメソッドは、@@iterator 既知の Symbol を使用してアクセスできます。このメソッドは、イテレータ オブジェクトを返します。

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

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

myIterator
;
> Array Iterator {}

イテレータで next() メソッドを呼び出すと、含まれる要素が一度に 1 つずつ順に処理され、呼び出しごとに 2 つのプロパティ(現在の要素の値を含む value と、イテレータがデータ構造の最後の要素を渡したかどうかを示すブール値)の done という 2 つのプロパティを含むオブジェクトが返されます。done の値が true になるのは、next() の呼び出しによって、イテレータ内の最後の要素を超える要素にアクセスしようとした場合のみです。

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 }

ジェネレータ関数

function\* キーワード(アスタリスクを使用)を使用して、ジェネレータ関数を宣言するか、ジェネレータ関数式を定義します。

function\* myGeneratorFunction() { };

イテレータと同様に、ジェネレータ関数は状態を維持します。ジェネレータ関数を呼び出すと、新しい Generator オブジェクトが返されますが、関数の本文でコードはすぐには実行されません。

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

myGeneratorObject
;
> Generator {  }

typeof myGeneratorObject;
> "object"

ジェネレータ オブジェクトはイテレータ プロトコルに従います。ジェネレータ関数の next() の呼び出しによって返される値は、yield 式によって決まります。この式は、ジェネレータ関数の実行を一時停止し、yield キーワードを含む式の値を返します。その後の next() の呼び出しにより関数の実行が継続され、次の yield 式で一時停止して関連する値を返します。

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 }

yieldreturn、または throw(エラーの場合)を使用してそれ以上の値が指定されなかった後に next() が呼び出されると、関数の本文の残りの部分が実行され、返されるオブジェクトの valueundefined で、done プロパティは 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 }

next() は、ジェネレータ関数自体ではなく、ジェネレータ関数が返すオブジェクトに対してのみ使用します。それ以外の場合、ジェネレータ関数を呼び出すたびに新しいジェネレータ オブジェクトが作成されます。

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

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

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

他の関数と同様に、ジェネレータ関数は return キーワードを検出すると停止します。次に、戻り値と、値 true を持つ done プロパティを含むオブジェクトを起動コンテキストに返します。

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 }

yield 式は識別子のセマンティクスの一部を受け取ることができ、ジェネレータ関数の一時停止された部分との間で双方向の「通信」を行うことができます。値がジェネレータの next() メソッドに引数として渡されると、前に一時停止された 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 }

これにより、前の yield に関連付けられた式全体が置き換えられることと、前の yield の値が 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 }

next() の最初の呼び出しに渡された引数はすべて、その値を受け入れる前の yield 式がないため無視されます。他の関数と同様に、最初のジェネレータ関数呼び出しに渡される引数は、ジェネレータ関数の本文のスコープ全体で使用できます。

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 }

yield\*(アスタリスクに注意)演算子は、別のジェネレータ関数などのイテラブルとともに使用され、オペランドが返す各値を反復処理して生成します。

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

JavaScript の実行は基本的に同期ですが、デベロッパーがイベントループを利用して非同期タスクを実行できるメカニズムがあります。

Promise

Promise は、Promise が作成された時点では未知の値のプレースホルダです。これは、非同期オペレーションを指示するコンテナ、オペレーションが成功または失敗とみなされる条件、いずれの場合も実行するアクション、その結果としての価値を指定するコンテナです。

new 演算子と組み込みの Promise コンストラクタ関数を使用して、Promise インスタンスを作成します。このコンストラクタは、executor という関数を引数として受け取ります。このエグゼキュータ関数は通常、1 つ以上の非同期アクションを実行するために使用されます。その後、Promise が正常に履行または拒否されたとみなされる条件を指定します。Promise は、エグゼキュータ関数の実行中に保留中として定義されます。エグゼキュータの終了後、エグゼキュータ関数と実行する非同期アクションが正常に完了すると Promise は完了(または一部のドキュメント ソースでは解決)され、エグゼキュータ関数でエラーが発生した場合や実行された非同期アクションが失敗した場合は拒否されます。Promise が履行または拒否されると、解決されたとみなされます。

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

コンストラクタは、2 つの引数を指定してエグゼキュータ関数を呼び出します。これらの引数は、Promise を手動で解決または拒否できる関数です。

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

Promise のフルフィルメントまたは拒否に使用される関数は、Promise の結果の値を引数(通常は拒否のエラー)として呼び出されます。

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", &lt;value>: "This Promise was successful." }

Promise チェーン

結果の Promise オブジェクトは、Promise コンストラクタから継承された then()catch()finally() の各メソッドを使用して操作できます。これらの各メソッドは Promise を返します。これを then()catch()、または finally() ですぐに操作でき、結果の Promise をチェーン化できます。

then() は、引数として 2 つのコールバック関数を提供します。最初の要素を使用して結果の Promise を遂行し、2 番目の要素を使用してそれを拒否します。どちらのメソッドも、結果の Promise に値を返す単一の引数を受け入れます。

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

また、then() を使用してフルフィルメント状態のみを処理し、catch を使用して不承認状態を処理することもできます。Promise の拒否メソッドで提供された値を含む単一の引数を使用して、catch を呼び出します。

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

Promise が履行または拒否されたときにハンドラ関数を実行できるようにする thencatch とは異なり、finally メソッドに引数として渡された関数は、Promise が履行されたか拒否されたかにかかわらず呼び出されます。ハンドラ関数は、Promise から渡された値を処理するものではなく、Promise の完了後にコードを実行することのみを目的としていないため、引数なしで呼び出されます。

同時実行

Promise コンストラクタには、Promise オブジェクトを含む iterable を使用して、複数の関連する Promise を操作する 4 つのメソッドが用意されています。これらのメソッドはそれぞれ Promise を返します。Promise は、渡された Promise の状態に基づいて解決または拒否されます。たとえば、Promise.all() は、このメソッドに渡されたすべての Promise が満たされた場合にのみ達成される Promise を作成します。

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

Promise の同時実行メソッドは次のとおりです。

Promise.all()
指定されたすべての Promise が満たされた場合にのみフルフィルメントになります。
Promise.any()
指定された Promise のいずれかが満たされた場合に満たされ、すべての Promise が拒否された場合にのみ拒否されます。
Promise.allSettled()
結果にかかわらず、Promise が解決するとフルフィルメントになります。
Promise.race()
最初に解決された Promise の結果に基づいて拒否または履行され、後で解決された Promise はすべて無視されます。

async/await

関数宣言または関数式の前で async キーワードを使用すると、関数が返す値はすべて、その値を含むフルフィルメント済み Promise として返されます。これにより、同期開発と同じワークフローを使用して非同期オペレーションを実行し、管理できます。

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

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

await 式は、関連する Promise が完了する間、非同期関数の実行を一時停止します。Promise が解決されると、await 式の値は、Promise の処理済みまたは拒否された値です。

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

await 式に含まれる Promise 以外の値はすべて、フルフィルメントの Promise として返されます。

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

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

理解度をチェックする

既知の数量を反復処理するには、どのループを使用しますか。

while
do...while
for