【jQuery => async/await】再帰で可変長の非同期処理の要求を順番に実行する

# はじめに
js勉強中に表題の実現に困ったので備忘録にまとめます。
要求数が一定ではない、非同期処理を順序立てて処理する方法です。
他のやり方もあるかもしれませんが、今回の場合は再帰呼び出しが便利だと思います。
同機能の実現をjQueryの書き方と``async/await``を用いた書き方の両方で試してみました。
どちらも結果は同じになります。

## Promise
非同期処理を作成する胴元的存在。async/awaitを使っても元をたどればこれに行きつく。

```js:promise.js
var sample = new Promise(function(resolve) { 
    resolve('true!');
 });
```
↑のように記述すればsampleは非同期処理として扱われます。次のasyncと性質は似ていてこれを簡略化したような考え方ですね。

## async
これを定義すると非同期関数となりPromiseを返します。```async function(){}```という形になります。
jQueryでいうところの$.ajax()などと同じように.then()でつなげられるようになります。
### async関数の返り値
**return**
返り値のPromiseはresolveとなる。

**throwや例外**
返り値のPromiseはrejectとなる。

## await
async関数の進みは、これをつけた処理の部分でいったんPromiseの結果が返されるまで待ちます。なのでなかでPromiseを使う。

# コード
## jQueryバージョン
```js:saiki.js
//jQuery $.Deferred()
var asyncTest=function(value){
    var def=$.Deferred();
    setTimeout(() => {
        def.resolve(value * 5);
    }, value * 200);
    return def.promise();
};

var order=[1,3,5,7,9];//テスト用の処理要求

var TestFunc=function(def){
    var req=order.shift();
    $.when(asyncTest(req))
        .then(
        function(data){
            console.log(data);
            if(order.length){
                TestFunc(def);
            }
            else{
                def.resolve('complete!!');
            }
        },
        function(jqXHR,textStatus,errorThrown){
            alert(textStatus+"_"+errorThrown);
            def.reject();
        }
    );
    return def.promise();
};
var def=$.Deferred();
TestFunc(def)
    .then((data) => {
    console.log(data);
});
```
## async/awaitバージョン
```js:saiki.js
//JavaScript async/await
const _asyncTest=(value) => {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve(value * 5);
        }, value * 200);
    });
};

const _order=[1,3,5,7,9];//テスト用の処理要求

const _TestFunc = async () => {
    var req=_order.shift();
    var data = await _asyncTest(req);
    console.log(data);
    if(_order.length){
        return await _TestFunc();
    }
    else{
        return 'complete!';
    }
};

_TestFunc()
    .then((data) => {
    console.log(data);
});
```
jQueryの方は特別なことはしていません。比較してみると、多少はasync/awaitの方が記述量が少なくなっています。これは大きな利点ですね。
ちなみにasync/awaitを利用したやり方で気を付けたほうがいいと思うところは↓

```js:saiki.js

const _TestFunc = async () => {
    var req=_order.shift();
    var data = await _asyncTest(req);
    console.log(data);
    if(_order.length){
        return await _TestFunc();//<=再帰呼び出しの際にawaitをつける
    }
    else{
        return 'complete!';
    }
};
```
一見すると、_TestFunc()はasyncの関数だから、呼び出し時にawaitをつけなくてもいいような…と思いました。awaitはasyncの中に書くものだからawaitをつけると_TestFunc()にはasyncとawaitを両方つけている気持ちになる。
ですが、つけないときちんと動きませんでした。

```js:saiki.js

const _TestFunc = async () => {
    var req=_order.shift();
    var data = await _asyncTest(req);//<=Promiseを返す
    console.log(data);
    if(_order.length){
        return await _TestFunc();
    }
    else{
        return 'complete!';
    }
};
```
↑この_TestFunc()の性質を考えたところ、中でasyncTest()を呼び出していて、これはPromiseを返します。awaitはPromiseを返すものにつけられるので、結果的にPromiseを返すものを内包しているので_TestFunc()にawaitをつけたら想定の動きとなります。

さらにasyncの中で処理終了まで待ちたい関数を呼び出すのだから、再帰関数も例外ではなくawaitはつけないといけないという当たり前の結論に行きつきました。コードの形を見るに**asyncの中にawaitがあるという形**になっていますので問題ないようです。

```js:saiki.js

const _TestFunc = async () => {
    var req=_order.shift();
    var data = await _asyncTest(req);
    console.log(data);
    if(_order.length){
        return await _TestFunc();//<=呼び出しの再帰元まで戻るためにreturnは忘れずに
    }
    else{
        return 'complete!';//<=同じくreturn
    }
};
```
もちろんreturnもつけます。これがないと再帰元に結果が返っていきません。_TestFuncにつないだ.then()から値を取り出したいときは忘れないようにします。

# おわりに
**jQueryのやり方**
メリット：慣れ親しんだやり方
デメリット：将来的に古くなる
 
**async/awaitのやり方**
メリット：コードがすっきりする
デメリット：バベルしないといけない

未来志向で行きたいと思うなら``async/await``を利用していくのもいいと思います。