Promiseへの翻訳コードでasync/awaitの挙動を理解する

# 対象とする読者
Promiseとasync/awaitをなんとなく理解している方。使ってみたけど挙動がよくわからなかったという方。

# 突然の仕変。同期から非同期に
こういう処理があったとして…

```javascript
function hoge() {
    // なんかかんか
    var foo = ...
    var bar = ...
    if (foo < ...) {
       return foo+bar;
    }
    var result = innerHoge(foo, bar);
    var lastResult = ... // result使ってごにょごにょ
    return lastResult;
}

function innerHoge(foo, bar) {
    ... // fooとbarをごにょごにょ
    return ...;
}

```

あとから、「innerHogeはXHRで得たデータを元にしないと有効なリターンを返せなくなってしまった！仕様変更だ！」 ってなったら、成功時と失敗時のコールバックを足してましたよね。以下はjQueryのajaxを使った例です。

```javascript
function innerHoge(foo, bar, successCallback, errorCallback) {
    // xhrでもなんでもいいけど成功と失敗かえす非同期の例として。fooとbarのごにょごにょは向こう側でやることに…
    $.ajax({
        url: "...",
        data: { foo: foo, bar: bar },
        success: function(data, status, xhr){
            successCallback(data);
        },
        error: function(xhr, status, ex){
            errorCallback(ex);
        }
    });
}
```

非同期で呼び出すからinnerHoge()の戻り値はhoge()に返せない。ピンチですね。呼び出し元のhoge()で引数のためのコールバック関数を作らないとならないし、そのhogeを呼び出すところも…と、影響が多くて泣きそうになります。

# コールバック引数の追加なんてさせてなるものか

これをまずはPromise型オブジェクトに任せてしまえば、コールバック引数のsuccessCallbackとerrorCallbackを追加せずに済みますね。Promiseのコンストラクタに渡す関数の第一引数が成功時のコールバック、第二引数が失敗時のコールバックとなり、then, catchに渡した関数に返されますからね。

```javascript
function innerHoge(foo, bar) {
    return new Promise((resolve, reject)=>{
        // xhrでもなんでもいいけど成功と失敗かえす非同期の例として。fooとbarのごにょごにょは向こう側でやることに…
        $.ajax({
            url: "...",
            data: { foo, bar },
            success: function(data, status, xhr){
                resolve(data);
            },
            error: function(xhr, status, ex){
                reject(ex);
            }
        });
    });
}
```

このように既存のコールバック系の関数はPromiseでラップすることで、個別の差異を共通のインターフェースにしてしまえます。慣れてくるとPromise返してくれて扱いやすいわーとなってきますよ。
ちなみにES6だと、{ foo: foo, bar: bar } なんていう同じ名前の代入は、 { foo, bar } と省略できます。

# いやでも戻り値型、変わっとるやんけ
はい。それでもhoge()は困ったままですね。引数は増えずに済みましたがPromiseなんて返されてもどうすんの？…となります。そこでasync/awaitの出番。こう書きちゃえばOKです。

```javascript
async function hoge() {
    // なんかかんか
    var foo = ...
    var bar = ...
    if (foo < ...) {
       return foo+bar;
    }
    var result = await innerHoge(foo, bar);
    var lastResult = ... // result使ってごにょごにょ
    return lastResult;
}
```
あら簡単！ functionの前にasyncを、innerHogeの呼び出しでawaitをつけるだけ！
どういうカラクリでしょう…。上記のhogeをPromiseを使って全く同じ挙動となるコードに翻訳すると次のようになるってことです。

```javascript
function hoge() {
    // なんかかんか
    var foo = ...
    var bar = ...
    if (foo < ...) {
        return Promise.resolve(foo+bar);
    }
    return innerHoge(foo, bar).then(function(result){
        var lastResult = ... // result使ってごにょごにょ
        return lastResult;
    });
}
```

awaitを使った戻り値は、本来innerHogeが返すPromiseオブジェクトのresolve値になります。また、awaitを使った場所から、そのasync function内のブロックの終わりまで、その値が使えてreturnまでできるように見えますが、実際にはthenで渡したThenable関数内にまるごと移動したような形となり、その返り値がresolve値となります。[^1]
また関数にasyncを付加した時点で、その関数が返すreturn値は必ずPromiseオブジェクトになります。そのため、return foo+barも、それをResolve値としたPromiseオブジェクトとなります。

# async/awaitつけまくりましょう

さて、これで async function hoge() ができましたが、さらにhoge()を呼び出している元も全部awaitにしないとならんとです。ただしそうする以上、そいつもasync functionブロック内じゃないといけない。async functionにしだしたら、async/awaitは伝播していくことになります。毒（蜜？）を喰らわば皿まで！面倒を見るしか！
ま、普通に.then, .catchで伝播を止めるって手もありますけど。

ちなみにPromiseに対応したinnerHogeですが、\$.ajaxがいただけないのでfetchに置き換えてしまいましょう。fetchはXHRや\$.ajaxのPromise版[^2]です。

```javascript
function innerHoge(foo, bar) {
    return fetch(`...?foo=${foo}&bar=${bar}`); 
}
```

すげースッキリ。上記ではasync functionではないですが、Promiseを返す関数であればawaitができますよ〜。でも読み手にわかりやすくするためにasync/awaitを付加することをお奨めします。[^3]

```javascript
async function innerHoge(foo, bar) {
    return await fetch(`...?foo=${foo}&bar=${bar}`); 
}
```

ちなみにawaitを使えばtry ... catch構文で、Promiseオブジェクトのreject値を例外処理として書けるようになるので、これまたスッキリですよ。

```javascript
(async function() {
    var result;
    try {
        result = await hoge(); 
    } catch(ex) {
        // ex = hoge()が返したPromiseオブジェクトのcatchに渡された値
    }
})();
```



[^1]: 意味合い的に同等のコードを示しているだけで具体的な実装はjavascriptエンジンに依るところです。実際にこんなコードが吐かれるわけではありません。
[^2]: と思って差し支えはないでしょう。厳密にはいろいろ違います。
[^3]: コードの中身を見なくても、async function→Promiseが返るのねと一瞬でわかるからってことです。