Async/Await を使って HTTP リクエストを送信する


概要
---

ES7 (ES2016) で Async/Await が導入される可能性が高いことから、HTTP リクエストの送信の練習をしました。

[2016年9月追記]

ES2017 で導入されました。

ブラウザーの対応状況
-----------------

Microsoft Edge のプレビュービルドおよび Firefox Nightly で利用可能な状態です。[v8](https://bugs.chromium.org/p/v8/issues/detail?id=4483) は実装に取り組んでいる最中です。TypeScript と Babel は対応しています。


Babel
-----

`package.json` を用意した上で、関連するプリセットとプラグインを導入します。あり得る組み合わせについては[「Transpile ES7 Async Await with Babel.js?」](http://stackoverflow.com/a/28709165/531320)の回答をご参照ください。

### ブラウザー対応

```bash
npm init -y
npm install --save \
babel-preset-es2015 \
babel-plugin-syntax-async-functions \
babel-plugin-transform-async-to-generator \
babel-plugin-transform-runtime
```

`babel` や `babel-node` を実行する際に使うプリセットとプラグインは `package.json` で指定することができます。

```js:package.json
{
  "babel": {
    "presets": [
      "es2015"
    ],
    "plugins": [
      "syntax-async-functions",
      "transform-async-to-generator",
      "transform-regenerator",
      "transform-runtime"
    ]
  }
}
```


### node.js 対応

node v5 を使う場合、ブラウザーと比べて、プリセットとプラグインを少なくすることができます。

```bash
npm init -y
npm install --save node-fetch
npm install --save-dev \
babel-preset-es2015-node@6 \
babel-plugin-syntax-async-functions \
babel-plugin-transform-async-to-generator
```

`babel-preset-es2015-node@6` の `@` の後ろの番号はNode.js のバージョンをあらわします。

`babel` や `babel-node` を実行する際に使うプリセットとプラグインは `package.json` で指定することができます。

```js:package.json
{
  "babel": {
    "presets": ["es2015-node"],
    "plugins": [
      "syntax-async-functions",
      "transform-async-to-generator"
    ]
  }
}
```

ほかに `babel-preset-es2015-node5` のようなパッケージもありますが、Node.js のバージョンを変更する場合に上述の　`package.json` を書き換える必要が生まれます。

トランスパイル
------------

### browserify

`browserify` コマンドを使ってモジュールを読み込んでみましょう。そのために `babelify` を導入します。

```bash
npm install --save-dev babelify
```

トランスパイルを実行するには次のコマンドを実行します。

```bash
browserify app.js -o bundle.js -t babelify
```

次のような HTML コードを用意してブラウザーで表示してみましょう。

```html
<html>
  <head>
    <meta charset="UTF-8">
  </head>
  <body>
    <script src="bundle.js"></script>
  </body>
</html>
```

### babel-node

node.js 版の場合、ちょっとしたコードを実行するのであれば、`babel-node` を使うことができます。

```bash
babel-node client.js
```

babel-node のインストールは次のコマンドを実行します。

```bash
npm install -g babel-cli
```

### jspm

[jspm](https://github.com/jspm/jspm-cli) は SystemJS をもとにしたパッケージ管理ツールです。記事の執筆時点では [SystemJS](https://github.com/systemjs/systemjs) は　ES6 で却下になった [Module Loader Polyfill](https://github.com/ModuleLoader/es6-module-loader) をもとにしています。記事の執筆時点で WHATWG が [Loader](https://github.com/whatwg/loader) の仕様策定を進めています ([Polyfill](https://github.com/caridy/loader.js))。

まずは npm で `jspm` コマンドをインストールします。

```bash
npm install -g jspm
```

プロジェクトのディレクトリを生成します。

```bash
jspm init -y
```

スクリプトを用意します。`import` 文が含まれていないとトランスパイルが実行されないので、空の `bootstrap.js` をつくります。

```bash
mkdir src
touch src/main.js
touch src/bootstrap.js
```

main.js の冒頭部分に `import` 文を書きます。

```js:bootstrap.js
export function bootstrap() {}
```

```js:main.js
import {bootstrap} from './bootstrap'
```

ソースコードをトランスパイルしてみましょう。並行してファイルを圧縮します。

```bash
jspm bundle src/main --inject --minify
```

次のような HTML を用意してブラウザーからアクセスしてみましょう。

```html
<!doctype html>
  <meta charset="UTF-8">
  <div id="container"></div>
  <script src="jspm_packages/system.js"></script>
  <script src="config.js"></script>
  <script>
    System.import('src/main.js');
  </script>
```


記事の執筆時点では jspm 0.16.19 では Babel 5 系が使われており、jspm の将来のバージョンで Babel 6 系に移行した場合、プリセットとプラグインを `config.js` で指定することが必要になる可能性があります。

Fetch API
---------

```js:client.js
(async () => {
  try {
    const url = 'http://httpbin.org/get';
    const response = await fetch(url);
    const json = await response.json();
    console.log(json.origin);
  } catch (error) {
    console.log(error);
  }
})();
```

通常の書き方は次のとおりです。

```js:client.js
fetch(url)
  .then(response => response.json())
  .then(json => {
    console.log(json.origin);
  })
  .catch(error => {
    console.log(error);
  });
```

node-fetch
----------

パッケージは次のコマンドで導入します。whatwg fetch の API と互換性をもちます。

```bash
npm install --save node-fetch
```

node.js で実行するスクリプトを用意してみましょう。

```js:client.js
const fetch = require('node-fetch');
const url = 'http://httpbin.org/get';

(async () => {
  try {
    const response = await fetch(url);
    const json = await response.json();
    console.log(json.origin);
  } catch (error) {
    console.log(error);
  }
})();
```

通常の書き方は上記のブラウザー版と同じです。

superagent
----------

パッケージは次のコマンドでインストールします。

```bash
npm install --save superagent
```

```js:client.js
const request = require('superagent');
const url = 'http://httpbin.org/get';

(async () => {
  try {
    const response = await request.get(url);
    console.log(response.body.origin);
  } catch (error) {
    console.log(error);
  }
})();
```

Promise 版は次のとおりです。

```js:client.js
request
  .get(url)
  .then(res => {
    console.log(res.body.origin);
  }, error => {
    console.log(error);
  });
```

request-promise
---------------

パッケージは次のコマンドでインストールします。

```bash
npm install --save request-promise
```

```js:client.js
const rp = require('request-promise');
const options = {
  uri: 'http://httpbin.org/get',
  json: true
};
  
(async () => {
  try {
    const response = await rp(options);
    console.log(response.origin);
  } catch (error) {
    console.log(error);
  }
})();
```

Promise 版は次のとおりです。

```js:client.js
rp(options)
  .then(res => {
    console.log(res.origin);
  })
  .catch(err => {
    console.log(err);
  });
```