コールバックを使った非同期実装について、分解してみるといろいろなエッセンスが詰まっていたのでまとめました。なぜ/どうやってコールバックを使うのか、理解する助けになれば幸いです。
Ajaxでデータを取得する関数getApiData()
を作ります。
完成形:
function getApiData(callback) {
$.ajax('/path/to/api')
.done(function (data) {
callback(null, data);
})
.fail(function (error) {
callback(error);
});
}
getApiData(function (error, data) {
// エラー時の処理
if (error) {
console.error(error);
return;
}
// 成功時の処理
console.log(data);
});
コールバックと非同期処理を中心に、この実装例について解説します。
1. コールバック
1-0. 普通の引数
これは普通の文字列を関数に渡している例です。
saySomething
の引数value
には、変数hello
に入れておいた文字列が渡されます。
function saySomething(value) {
console.log(value);
}
var hello = 'Hello!!';
saySomething(hello);
1-1. 引数に関数を渡す
今度は変数hello
に文字列ではなく、関数を代入してdoSomething()
に渡しています。doSomething
内の引数callback
は関数なので、callback()
と書けば実行することができます。
function doSomething(callback) {
callback();
}
var hello = function () {
console.log('Hello!!');
};
doSomething(hello);
このように別の関数の中で実行されるように渡された関数のことをコールバックといいます。
1-2. 無名関数を直接渡す
先ほどは変数hello
に関数を格納してdoSomething()
を呼び出していましたが、変数化せずに無名関数をそのまま渡すこともできます。実行結果は同じです。
function doSomething(callback) {
callback();
}
doSomething(function () {
console.log('Hello!!');
});
1-3. コールバックに引数を渡す
getSomething
内のcallback
は関数なので、callback('Hello!!')
と書けば実行時に値を渡すことができます。この値は無名関数内の引数value
へと伝わります。
function getSomething(callback) {
callback('Hello!!');
}
getSomething(function (value) {
console.log(value);
});
2. 非同期処理
2-0. 同期処理で値を取得する
今までの例では、AjaxやsetTimeout
のような非同期処理を含んでいなかったので、わざわざコールバックを使う必要性はありませんでした。1-3のコードは、こう書いたほうが自然です。
function getSomething() {
return 'Hello!!';
}
var value = getSomething();
console.log(value);
ですが、もしgetSomething
が非同期でしか処理できない場合、return
を使って値を返すことができません。
うまくいかない例:
function getSomething() {
setTimeout(function () {
return 'Hello!!';
}, 1000);
}
var value = getSomething(); // この方法では値が取れない
console.log(value);
ここでコールバックを使った値の受け渡しが必要になります。
※ECMAScript 6ではコールバックの代わりにPromiseでも非同期処理を扱うことができます。
2-1. コールバックで非同期処理の値を取得する
1-3にsetTimeout
を追加しただけのコードです。呼び出し側(後半のコード)は何も変わっていません。
function getSomething(callback) {
setTimeout(function () {
callback('Hello!!');
}, 1000);
}
getSomething(function (value) {
console.log(value);
});
コールバックを使うと、getSomething
内の処理が非同期処理になっても(もちろん同期処理だったとしても)同じようにvalue
へと値を渡すことができます。
2-2. ajaxの結果を取得する
もう少し実践的な例にしましょう。非同期処理の内容をAjaxにしてみます。Ajaxが完了すると、コールバックを通してconsole.log()
に取得したデータが渡ります。
function getApiData(callback) {
$.ajax('/path/to/api')
.done(function (data) {
callback(data);
});
}
getApiData(function (data) {
console.log(data);
});
2-3. ajaxのエラー処理を追加する
2-2では、Ajaxが成功したときはコールバックが実行されますが、失敗したときは何も起きませんでした。.fail()
を追加して、Ajaxが失敗してもcallback()
が呼ばれるようにします。
function getApiData(callback) {
$.ajax('/path/to/api')
.done(function (data) {
callback(data);
})
.fail(function (error) {
callback(error);
});
}
getApiData(function (data) {
console.log(data); // dataは取得したデータ?それともエラー?
});
しかし、このままだとエラーが起きたのか、正常にデータが取れたのか判別が難しい状態です。
エラー判定をコールバック側へと伝えるために、callback()
の引数を2つに増やして、第1引数にはエラーのオブジェクトを、第2引数には取得したデータを渡すように変更します。正常時は第1引数にnull
を渡し、エラーがないことを明示します。
function getApiData(callback) {
$.ajax('/path/to/api')
.done(function (data) {
callback(null, data);
})
.fail(function (error) {
callback(error);
});
}
getApiData(function (error, data) {
// エラー時の処理
if (error) {
console.error(error);
return;
}
// 成功時の処理
console.log(data);
});
これにて完成です!
コールバックを活用し、非同期のデータ取得をエラー判定付きで実装することができました。