関数 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が解決するまで待機しています。