javascript非同期処理~Promise,async,awaitとコールバック関数~

# きっかけ
仕事で`vue.js`を使うことになって非同期処理について勉強していたところ、`Promise`とか`async`とか`await`とかで?????となったのでふんわりな理解だったコールバック関数について学び直そうと決意

# 現状の私の理解
- `コールバック関数`は関数に入れる関数。それ以外は...

# 非同期処理でやれることの例
- ボタンがクリックされた後に何かを表示する
- ファイルの読み込みが完了した後に何かをする

# 非同期処理のイメージ
- <font color="Salmon">処理</font>を関数単位で考えた時
  - 実行中の関数の中で非同期処理が発火する
  - 実行中の関数の終了を持ってコールバック関数を実行する
<img width="563" alt="スクリーンショット 2019-10-02 8.07.51.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303093/9f98dd97-bbca-f501-8bd6-fa1b2dd9d199.jpeg">

※こちらのイメージは拾い画なのですが拾い元が特定できなくてリンクを載せることができません。。。知ってる方いましたら教えて...

# 関数を実行するときと値として扱う際の書き方の違い
- カッコ【()】をつけるかつけないか

```javascript:hoge.js
const max1 = Math.max(1, 2); // これはカッコがついてるので関数の計算結果が入る
const max2 = Math.max; // これはカッコがないので関数自体が入る
```

- カッコなしを実行しようとすると`not a function`エラーが発生する

# 相手に実行してもらうのがコールバック関数
- 以下で考えるとconsole.logがコールバック関数となる。

```javascript:huga.js
setTimeout(function() {
  console.log('Hello!');
}, 2000);
```

# 関数を受け取る関数を高階関数と呼ぶ
- さっきのコードで言うと`setTimeout`が高階関数。

# 非同期処理の書き方
- 1.非同期処理関数はコールバック関数を受け取る高階関数にする
- 2.利用者は「終わったら実行したい処理」をコールバック関数として渡す
- 3.非同期処理関数は処理が終わったらコールバック関数を呼び出す

# DOMのイベントで非同期処理
- クリックイベントにコールバック関数（console.log)を紐付け

```javascript:piyo.js
document.querySelector('.my-button').addEventListener('click', function(event) {
  console.log('clicked!');
});
```

- 関数名でも紐付け可能
- 
```javascript:puyo.js
function callback(event) {
  console.log('Hello'!);
}

document.querySelector('.my-button').addEventListener('click', callback);
```

# 非同期処理を記述するPromiseの基本
- 今までの内容で非同期処理には高階関数とコールバック関数が必要なことがわかったと思うが、処理が多くなるとネストが多段になって読みにくくなってしまう。
- そこで`Promise`と言う仕組みを使うことで簡潔に記述できる
- 使い方とサンプルは以下
  - 1.まずPromiseをnewすることによりPromiseオブジェクトを作成。
  - 2.Promiseのコンストラクタには、実行したい処理を書いた関数を渡し、処理が済んだらresolve関数を呼び出すことで終了を明示する。
  - 3.Promiseオブジェクトのthenメソッドに、Promise終了後に処理したい関数を渡す。
  - 4.「Promiseの実行が済んだ後にxxxする」

```javascript:promise.js

const promise = new Promise((resolve, reject) => {
    setTimeout(() => { 
        console.log('hello');//実行したい処理
        resolve();//実行したい処理が終わったことを明示。thenメソッドへ。
    }, 500);
});

promise.then(() => console.log('world!'));

```

# Promise〜値を渡す1〜
- resolveには値を渡すこともできる
- resolve関数に渡した値は、thenメソッドで受け取ることができる。
- 以下サンプル(XHRのレスポンスをresolve関数に渡している)

```javascript:resolve.js
const promise = new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', 'foo.txt');
    xhr.addEventListener('load', (e) => resolve(xhr.responseText));
    xhr.send();
});

promise.then((response) => console.log(response));
```

# Promise〜値を渡す2〜
- `reject`は`resolve`同様、値を渡すことができる
- `reject`が呼ばれたら`catch`が実行されてエラー終了させる

```javascript:reject.js

const promise = new Promise((resolve, reject) => reject('error'));

promise.then(() => console.log('done')) // thenは実行されない
       .catch((e) => console.log(e)); // 「error」とだけ表示される

```


# thenを繋げる`thenチェーン`!!!
- これがPromiseのいいところみたいでthenを繋げて書くことができる。

```javascript:then.js

function printAsync(text, delay) {
    const p = new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log(text);
            resolve();
        }, delay)
    });
    
    return p;
}

printAsync('hello', 500)
//500ミリ秒ごとに「hello」「world」「lorem」「ipsum」と表示する
    .then(() => printAsync('world', 500))
    .then(() => printAsync('lorem', 500))
    .then(() => printAsync('ipsum', 500));

```

### Promiseを使わない時と使った時のソース比較サンプル（ファイルオープン)
- 使わないとき

```javascript:nothen.js
function openFile(url, onload) {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', url);
    xhr.addEventListener('load', (e) => onload(e, xhr));
    xhr.send();
}

openFile('foo.txt', (event, xhr) => {
   openFile('bar.txt', (event, xhr) => {
     openFile('baz.txt', (event, xhr) => {
         console.log('done!');
     });
   });
});
```

- 使った時

```javascript:then.js

function openFile(url) {
    const p = new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open('GET', url);
        xhr.addEventListener('load', (e) => resolve(xhr));
        xhr.send();
    });
    
    return p;
}

openFile('foo.txt')
    .then((xhr) => openFile('bar.txt'))
    .then((xhr) => openFile('baz.txt'))
    .then((xhr) => console.log('done!'));

```

# 複数のPromiseの終了を待つPromise.all
- 上記と同じ処理を書き換えてみる
- 配列で渡してthenは1個で書ける。
- thenの結果は配列で返ってくるため注意

```javascript:promise.all
function openFile(url) {
    const p = new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open('GET', url);
        xhr.addEventListener('load', (e) => resolve(xhr));
        xhr.send();
    });
    
    return p;
}

const promise = Promise.all([openFile('foo.txt'),
                             openFile('bar.txt'),
                             openFile('baz.txt')]);
promise.then((xhrArray) => console.log('done!'))
```


# async関数とawait
- やっと来ました。これについて知りたくて遡りしてたら結構知ってないといけないことが多かった。。。

- awaitはPromiseを同期的に展開する（ように見せかける）機能。
- <font color="Salmon">Promiseオブジェクトを返す関数を呼び出す前につけると取得を待ってから次の処理へ行ってくれる</font>
- awaitは仕様条件があり、<font color="Red">asyncがついた関数の中でしか利用できない</font>と言う条件がある。
- さっきのファイルオープンの処理をasync/awaitを使って書くと以下のようになる

```javascript:async.js

function openFile(url) {
    const p = new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open('GET', url);
        xhr.addEventListener('load', (e) => resolve(xhr));
        xhr.send();
    });
    
    return p;
}

async function loadAllFiles() {
    //ファイルオープンを待って次の処理へ
    const xhr1 = await openFile('foo.txt');
    //ファイルオープンを待って次の処理へ
    const xhr2 = await openFile('bar.txt');
    //ファイルオープンを待って次の処理へ
    const xhr3 = await openFile('baz.txt');
    console.log('done!');
}

loadAllFiles();

```

# async/awaitのエラー検出
- try-catchが書けるのでその中に書いていく

```javascript:async.js

function openFile(url) {
    const p = new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open('GET', url);
        xhr.addEventListener('load', (e) => resolve(xhr));
        xhr.send();
    });

    return p;
}

async function loadAllFiles() {
  try {
    //ファイルオープンを待って次の処理へ
    const xhr1 = await openFile('foo.txt');
    //ファイルオープンを待って次の処理へ
    const xhr2 = await openFile('bar.txt');
    //ファイルオープンを待って次の処理へ
    const xhr3 = await openFile('baz.txt');
    console.log('done!');
  } catch(error) {
    const {
      status,
      statusText
      } = error.response;
      console.log(`Error! HTTP Status: ${status} ${statusText}`);
    }
  }
}

loadAllFiles();


```

# あとは書いて覚える！！！！！