JavaScriptのコールバック関数やPromise周りの勉強をする際にわかりやすかったサイトと備忘をまとめます。
コールバック関数について
おすすめ→JavaScriptの「コールバック関数」とは一体なんなのか
以下は記事を読みながら実際にかいてみたコードです。
特に驚きだったのは変数に関数を代入できる(var sum = add
)ところですね、これは他の言語ではまずみないです。
function add(a,b) { return a + b; } // 変数に既存の関数を代入する var sum = add; // 変数自体に関数を指定する(無名関数) var sumA = function(a, b) { return a + b }; // 関数の中から関数を呼び出す var sumB = function(a, b) { return add(a, b) }; // 変数自体に関数を指定する(アロー関数) var sumC = (a, b) => { return a + b }; // 関数の中から関数を呼び出す var sumD = (a, b) => { return add(a, b) }; console.log(sum(1, 2)) console.log(sumA(1, 2)) console.log(sumB(1, 2)) console.log(sumC(1, 2)) console.log(sumD(1, 2))
結果はすべて3と出力される
次に試したところも驚きで、関数に対して関数を渡せるのもこれまでのプログラミングで培ったものと直感に反していました。var sumE = function a(func) { return func(1, 2); }
// ----------------------------- // 通常の関数+引数に関数を受け取る // 関数を受け取る関数を「高階関数」という var sumE = function a(func) { return func(1, 2); } // アロー関数+引数に関数を受け取る // 関数を受け取る関数を「高階関数」という var sumF = (func) => { return func(1, 2); } // add関数をsumE/sumFに渡して計算してもらう console.log(sumE(add)) console.log(sumF(add)) // -----------------------------
結果はすべて3と出力される
// コールバック関数とは「高階関数に渡すための関数」 // console.log(sumE(add)) における add のこと setTimeout(function() { console.log('Hello!');}, 2000);
2000ミリ秒後にHello!と表示される
Promiseについて
Promiseの前にコールバック地獄について知る
コールバック関数を多用するとどんどんネストが深くなることで可読性が落ちたり、バグの温床になる可能性があります。
test1(function(data1) { test2(function(data2) { test3(function(data3) { // 何かの処理 } } }
これをコールバック地獄と呼びます。
Promiseはこれを解消するための方法です。
参考:Promiseとasync/awaitでJavaScriptの非同期処理をシンプルに記述する
Promiseの使い方
promise = new Promise(function (resolve, reject) { console.log(123) resolve(); }); promise.then( console.log(456) );
このコードを実行すると以下のように表示されます。
123 456
さて、何が起きているのかを順を追ってみていきましょう。
まずはここですね。
promise = new Promise(function (resolve, reject) { console.log(123) resolve(); });
ここではPromise型
の変数promise
を定義しています。(名前はなんでも良いです)
promise
の第1引数にはコールバック関数が指定されておりconsole.log(123)
とresolve()
というものがありますね。console.logは画面に表示するだけのものなのでresolve()
について考えてみましょう。
resolve()
は「解決する」という意味を持ちますが、ここでのresolve()
の役割は変数promiseで規定されていた処理が終わったので次の処理に進んでいいよというものです。
resolve()
が呼び出されると次はthen句
に移動します。
この処理ではthen
の中にある処理を実行しているだけですね。
promise.then( console.log(456) );
よって以下のように表示されるわけです。
123 456
Promiseをうまく使うとこのようにスッキリ書くことができます。
function printAsync(text, delay) { const p = new Promise((resolve, reject) => { setTimeout(() => { console.log(text); resolve(); }, delay) }); return p; } printAsync('hello', 500) .then(() => printAsync('world', 500)) .then(() => printAsync('lorem', 500)) .then(() => printAsync('ipsum', 500));
Promiseとasync/awaitでJavaScriptの非同期処理をシンプルに記述するよりコードを引用
ちなみに、then以降に変数を渡す場合は以下のようにすることでresolve()
経由で変数を渡すことができます。
promise = new Promise(function (resolve, reject) { console.log(123) abc = 456 resolve(abc); }); promise.then(function(val){ console.log(val); });
Promiseでもネストは起きる
しかし共通化できない処理の場合はPromise自体がネストしてしまいます。
new Promise((resolve) => { resolve(3); }).then((v1) => { // v1 は 3 new Promise((resolve) => { resolve(v1 * 3); }).then((v2) => { // v2 は 9 new Promise((resolve) => { resolve(v2 * 4); }).then((v3) => { // v3 は 36 console.log(v3); // 36 が出力される }); }); });
これはこまりましたね。
Promiseにはthen関数に渡す関数内でreturnを使うことで以下のようにできます。
new Promise((resolve) => { resolve(3); }).then((v1) => { // v1 は 3 return new Promise((resolve) => { resolve(v1 * 3); }); }).then((v2) => { // v2 は 9 return new Promise((resolve) => { resolve(v2 * 4); }); }).then((v3) => { // v3 は 36 console.log(v3); // 36 が出力される });