JavaScriptの学習・参考リファレンス/ドキュメント

JavaScript、ECMAScriptの学習と参考メモ 入門~初心者~中級者~上級者を目指して

関数 function

JavaScriptでの関数定義の方法についてです。

functionを使用した関数定義

JavaScriptではキーワードfunctionを使って関数の定義を行えます。

  function f () {
    console.log(1);
  }
  f(); // 1

括弧内に関数に渡される引数を設定できます。 引数はカンマ区切って複数指定できます。

  function f (a, b) {
    console.log(a + b);
  }
  f(1,2); // 3

JavaScriptの関数は第一級関数で、参照として持つ事ができます。

  let f = function (v) {
    console.log(v);
  };

  f('AAA'); // AAA

可読性のため、functionの後に名前を入れておく事も可能です。

  let f = function functionname (v) {
    console.log(v);
  };
  
  f('AAA'); // AAA

yieldとアスタリスク*を使ったジェネレーター関数

反復して値を返す処理は、ジェネレーター関数を使って表現できます。
nextが呼ばれる度に、yield の値が1つずつ返却されるイメージです。

ジェネレーター関数を示すために、functionキーワードの後にアスタリスクをつけます。
返却値はreturnではなくyieldを使います。

function* generator(i) {
  yield 2 * i;
  yield 3 * i;
  yield 5 * i;
  yield 7 * i;
}

const g = generator(2);
console.log(g.next().value); // 4
console.log(g.next().value); // 10

whileと合わせた使用例は以下の通りです。

function* squareIterator(number) {
  for (let i = 1; i <= number; i++){
   yield i * i;
  }
}

const it = squareIterator(5);

let result = it.next();
while (!result.done) {
 console.log(result.value); // 1, 4, 9, 16, 25
 result = it.next();
}

アロー関数

関数を短く表現できる構文です。

引数1, 引数2, …) => { 処理 };

これは次の関数と同じ意味です。

function引数1, 引数2, …){ 処理 };

値を返す関数の場合、処理の最後はreturnとなりますが、その場合特に次のように省略できます。

引数1, 引数2, …) => 返却する値;

具体例は次の通りです。

let f1 = (n) => n * n * n;
console.log(f(5)); // 125

let f2 = (n) => {return n * n * n};
console.log(f2(5)); // 125

let f3 = (n) => {console.log(n);};
f3(5); // 5

非同期関数 async function

async functionキーワードで非同期の関数を定義できます。 (このメソッドをよりよく理解するには、Promisenオブジェクトについて知っている必要があります)

asyncを付けた非同期関数は、Promise型を返すようになります。 また、非同期関数内ではawait式を含むことが出来るようになります。

以下は非同期関数を使う場合の例をあげます。

最初に一定時間後に処理を行う関数を用意します。 timeミリ秒後に、valueを含む文字列を返す関数です。

function resolveTimer(time,value) {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log(`resolveTimer ${value}`);
      resolve(`resolved ${value}`);
    }, time);
  });
}

これを使って、以下のようにawaitを付与して呼び出しを行ってみます。

async function asyncCallA() {
  console.log('asyncCall begin');
  const resultA = await resolveTimer(1000,'A');
  const resultB = await resolveTimer(500,'B');
  console.log(resultA);
  console.log(resultB);
  console.log('asyncCall exit');
  return [resultA,resultB];
}
(async function(){
  let a = await asyncCallA();
  console.log(a);
})();
/**
=>
"asyncCall begin"
Promise {<pending>}
"resolveTimer A"
"resolveTimer B"
"resolved A"
"resolved B"
"asyncCall exit"
["resolved A", "resolved B"]
*/

asyncCallAでは、各resolveTimerの解決をawaitのタイミングで待つことになり、逐次出実行がされます。
1つ目のresolveTimerが解決するまで時間を待ってresultAには文字列が格納され、 その後2つ目のresolveTimerが解決されresultBには文字列が格納されます。

今回は、asyncCallAの呼び出し元でawait asyncCallA()としているので、
2つのresolveTimerが完了するまで処理が止まりますが、
awaitしないで呼び出せば、そのまま後続の処理に実行が進みます。

次はawaitを使わないで呼び出した場合の例です。

async function asyncCallB() {
  console.log('asyncCall begin');
  const resultA = resolveTimer(1000,'A');
  const resultB = resolveTimer(500,'B');
  console.log(await resultA);
  console.log(await resultB);
  console.log('asyncCall exit');
  return [resultA,resultB];
}
(async function(){
  let a = await asyncCallB();
  console.log(a);
})();
/**
=>
"asyncCall begin"
Promise {<pending>}
"resolveTimer B"
"resolveTimer A"
"resolved A"
"resolved B"
"asyncCall exit"
(2) [Promise, Promise]
*/

asyncCallBの場合、resolveTimerの呼び出しは2つとも即時行われます。

await resultAのタイミングで1つ目のresolveTimerが解決するまで時間を待ちますが、
その時点で2つ目のresolveTimerの処理も進められているのでasyncCallAより早く全体が終わります。

resolveTimer2つともの呼び出しが終わるまで待機するには、await Promise.allを使うことができます。

async function asyncCallC() {
  console.log('asyncCall begin');
  const resultA = resolveTimer(1000,'A');
  const resultB = resolveTimer(500,'B');
  const result = await Promise.all([resultA,resultB]).then(m => m);
  console.log(result);
  console.log('asyncCall exit');
  return result;
}
(async function(){
  let a = await asyncCallC();
  console.log(a);
})();

/**
=>
"asyncCall begin"
Promise {<pending>}
"resolveTimer B"
"resolveTimer A"
["resolved A", "resolved B"]
"asyncCall exit"
["resolved A", "resolved B"] 
*/

asyncCallCでは、await Promise.allを使い、全てのresolveTimerが解決するまで待機しています。

更新日 : 2021年01月04日   作成日 : 2020年06月07日