JavaScript入門 その7：非同期処理、モジュール

JavaScript入門の書籍を読んで書いてあったこと＋自分で調べたことのメモ書きその7。非同期処理、モジュール（export、import）。


# 非同期処理

### fetchとPromiseオブジェクト

#### fetch
`fetch(resource[, options])` はネットワークから指定したリソースを取得する関数である。
引数はURL、Requestオブジェクト。
options でリクエストメソッドの指定などができる。
リソースが取得できたらPromiseオブジェクトを返す。

#### Promise.protype.then
Promise のメソッド `then(onFulfilled[, onRejected])` は最大2つの引数を取る。1番目の引数は処理が成功した（Promiseが履行された場合）のコールバック関数、2番目の引数は処理が失敗した場合（Promiseが拒否された）場合のコールバック関数である。
戻り値はPromiseで、then()メソッドのチェーンにより連続した非同期処理を記述することができる。

```js
let result = document.querySelector("#result");
let url = "/test.json";
fetch(url)
  .then((response) => {
    if (!response.ok) {
      throw new Error("failed.");
    }
    return response.json();
  })
  .then((data) => {
    result.innerText = data.text;
  });
```

前のthen の引数である無名関数の戻り値が次のthenの引数になる。
4行目のthenの引数 response はReponseオブジェクト、10行目のthenの引数dataはJSONのオブジェクトである。
`response.json()` の戻り値はPromiseオブジェクトである。
fetch が成功したかのチェックは5行目のように `response.ok` で行う。404の場合はPromiseは履行されるので。


### async/await
async/awaitを使うことによってfetchとPromiseのthenを使った記述よりもさらに通常の処理に近い形で非同期処理を記述できる。
async/awaitでは非同期処理を行いたい処理を非同期化数の中にまとめて書く。関数定義の前に`async`を付けると非同期関数となる。非同期関数内の処理で`await`のところでPromiseが決定（履行または拒否される）まで一時停止する。
先のfetchとPromiseのthenを使った例をasync/awaitを使って書き換えると下記になる。3行目にawaitいるかな？とも思ってしまうが`reponse.json()`（Responseのインスタンスメソッド）の戻り値はPromiseオブジェクトなので必要なのだろう。ようはPromiseオブジェクトを返すところにawaitがいると考えればいいのではないかと思う。たぶん。

```js
async function getTestJSON(url) {
  let response = await fetch(url);
  let data = await response.json();
  let result = document.getElementById("result");
  result.innerText = data.text;
}

getTestJSON("/test.json");
```


# モジュール
HTMLにscriptタグを複数書けば複数のJavaScriptファイルを読み込むことができる。しかしその場合は同名の変数、関数があると名前が衝突する。
この問題はモジュールで解決できる。モジュールはスコープが独立しておりモジュール間では関数などの定義の段階では名前の衝突が起こらない。
。
モジュールの使用にはimportとexportを利用する。HTMLでは**scriptタグのtype属性をmoduleとする**。

※importとexportでモジュールを使う方式は `ES Modules`。これとは別に `CommonJS` というモジュールを使う仕組みがある。よく名前を聞く `node.js` は CommonJS を使っている。

#### export
`export { 名前1 [as 別名1, 名前2... }`
波括弧の中にカンマ区切りでエクスポートするモジュールの定数名、関数名、クラス名を書く。`as`で別名を付けてエクスポートすることもできる。

下記のように宣言と同時にエクスポートすることもできる。

```js
export const c1 = "abc";

export function someFunc() {
  console.log("someFunc");
}
```

デフォルトエクスポートというものがあるが、そちらはとりあえず詳細は触れずにおく（正直よくわからない）。

#### import
`import { 名前1 [as 別名1, 名前2... } from モジュールのファイルのパス`
`import * from モジュールのファイルのパス`
波括弧の中にカンマ区切りでインポートする定数名、関数名、クラス名を書く。`as`で別名を付けてインポートすることもできる。波括弧の部分を`*`にすると指定したモジュールでエクスポートされているものすべてをインポートする。

`import * from as モジュールオブジェクト名 from モジュールのファイルのパス`
`as` でモジュールオブジェクトを作成すると、エクスポートしたものを `モジュールオブジェクト名.エクスポートしたものの名前` で利用できる。

例：
module_a.js：`testFunc`と`maskObject`をエクスポートしている。`maskObject`には`someObject`という別名を付けている。

```js
const testFunc = () => {
  console.log("module_a testFunc!");
};

const maskObject = {
  show() {
    console.log("module_a maskObject!");
  },
};

const innerFunc = () => {
  console.log("innerFunc!");
}

export { testFunc, maskObject as someObject };

console.log("module_a export side");
```

module_b.js：`testFunc`をエクスポートしている。modulea.js でエクスポートした関数と同じ名前。

```js
const testFunc = () => {
  console.log("module_b testFunc!");
};

export { testFunc };

console.log("module_b export side");
```

import.js：`module_a.js`, `module_b.js`でエクスポートされた関数、オブジェクトをインポートしたして利用している。module_b.js からモジュールオブジェクト`moduleB`を作成して利用している。

```js
import { testFunc, someObject } from "/js/modules/module_a.js";
import * as moduleB from "/js/modules/module_b.js";

testFunc();
someObject.show();

moduleB.testFunc();

console.log("import side");
```

HTMLファイル：scriptタグで `type="module"` としている。

```html
<script src="/js/import.js" type="module"></script>
```

実行結果（consoleへの出力）

```console
module_a export side
module_b export side
module_a testFunc!
module_a maskObject!
module_b testFunc!
import side
```


# 参考情報

### 書籍
[解きながら学ぶJavaScriptつみあげトレーニングブック | マイナビブックス](https://book.mynavi.jp/ec/products/detail/id=126984 "解きながら学ぶ JavaScriptつみあげトレーニングブック | マイナビブックス") 9章

### WEBページ
[フェッチ API の使用 - Web API | MDN](https://developer.mozilla.org/ja/docs/Web/API/Fetch_API/Using_Fetch)
[Promise - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise)
[Response.json() - Web API | MDN](https://developer.mozilla.org/ja/docs/Web/API/Response/json)
[JavaScript モジュール - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Modules)
[CommonJSとES Modulesについてまとめる](https://zenn.dev/yodaka/articles/596f441acf1cf3)