コールバックを使った非同期処理の実装例

コールバックを使った非同期実装について、分解してみるといろいろなエッセンスが詰まっていたのでまとめました。なぜ／どうやってコールバックを使うのか、理解する助けになれば幸いです。

Ajaxでデータを取得する関数`getApiData()`を作ります。

**完成形:**

```js
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`に入れておいた文字列が渡されます。

```js
function saySomething(value) {
  console.log(value);
}

var hello = 'Hello!!';

saySomething(hello);
```

### 1-1. 引数に関数を渡す
今度は変数`hello`に文字列ではなく、関数を代入して`doSomething()`に渡しています。`doSomething`内の引数`callback`は関数なので、`callback()`と書けば実行することができます。

```js
function doSomething(callback) {
  callback();
}

var hello = function () {
  console.log('Hello!!');
};

doSomething(hello);
```

このように別の関数の中で実行されるように渡された関数のことをコールバックといいます。

### 1-2. 無名関数を直接渡す
先ほどは変数`hello`に関数を格納して`doSomething()`を呼び出していましたが、変数化せずに無名関数をそのまま渡すこともできます。実行結果は同じです。

```js
function doSomething(callback) {
  callback();
}

doSomething(function () {
  console.log('Hello!!');
});
```

### 1-3. コールバックに引数を渡す
`getSomething`内の`callback`は関数なので、`callback('Hello!!')`と書けば実行時に値を渡すことができます。この値は無名関数内の引数`value`へと伝わります。

```js
function getSomething(callback) {
  callback('Hello!!');
}

getSomething(function (value) {
  console.log(value);
});
```

## 2. 非同期処理

### 2-0. 同期処理で値を取得する
今までの例では、Ajaxや`setTimeout`のような非同期処理を含んでいなかったので、わざわざコールバックを使う必要性はありませんでした。1-3のコードは、こう書いたほうが自然です。

```js
function getSomething() {
  return 'Hello!!';
}

var value = getSomething();
console.log(value);
```

ですが、もし`getSomething`が非同期でしか処理できない場合、`return`を使って値を返すことができません。

**うまくいかない例:**

```js
function getSomething() {
  setTimeout(function () {
    return 'Hello!!';
  }, 1000);
}

var value = getSomething();   // この方法では値が取れない
console.log(value);
```

ここでコールバックを使った値の受け渡しが必要になります。
※ECMAScript 6ではコールバックの代わりに[Promise](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise)でも非同期処理を扱うことができます。

### 2-1. コールバックで非同期処理の値を取得する
1-3に`setTimeout`を追加しただけのコードです。呼び出し側（後半のコード）は何も変わっていません。

```js
function getSomething(callback) {
  setTimeout(function () {
    callback('Hello!!');
  }, 1000);
}

getSomething(function (value) {
  console.log(value);
});
```

コールバックを使うと、`getSomething`内の処理が非同期処理になっても（もちろん同期処理だったとしても）同じように`value`へと値を渡すことができます。

### 2-2. ajaxの結果を取得する
もう少し実践的な例にしましょう。非同期処理の内容をAjaxにしてみます。Ajaxが完了すると、コールバックを通して`console.log()`に取得したデータが渡ります。

```js
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()`が呼ばれるようにします。

```js
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`を渡し、エラーがないことを明示します。

```js
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);
});
```

これにて完成です！
コールバックを活用し、非同期のデータ取得をエラー判定付きで実装することができました。