TypeScript の async/await を理解する　その２ async/await

前の２つのエントリは、このエントリを調べたいための準備だったのです。

* [Javascript の yield と generator](http://qiita.com/TsuyoshiUshio@github/items/18a0fee6034890b8ab03)
* [TypeScript の async/await を理解する　その１ Promise](http://qiita.com/TsuyoshiUshio@github/items/2b9d6459827dfcd18201)

やっと本編のasync / await について書くことができます。

# Promise で非同期処理の順序性を保証してみる。

Promise　によって、非同期処理の書きやすさが一段アップしましたが、async/wait の登場によってさらに素晴らしいものになりました。まずは、Promise で非同期のコード書いてみます。

```javascript
function waitAndAnswer(message:string) : Promise<any> {
    console.log("Wait for 3 second.");
    return new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log(`You said ${message}`);
        resolve();
    }, 3000);
    });
}

console.log("step 1");
waitAndAnswer("hi");
console.log("step 2");
waitAndAnswer("Konnnichiwa");
console.log("end");
```
これを実行すると

```
step 1
Wait for 3 second.
step 2
Wait for 3 second.
end
You said hi
You said Konnnichiwa
```

これを順序性を保証するように書き換えてみましょう。

先ほどのコードのの、`waitAndAnswer()` メソッドの呼び出し部分を次のように変えます。

```javascript
console.log("step 1");
waitAndAnswer("hi").then(()=> {
    console.log("step 2");
    return waitAndAnswer("konnichiwa"); // NOTE: Promise を戻している
}).then(() => {
    console.log("end");
});
```
```
step 1
Wait for 3 second.
You said hi
step 2
Wait for 3 second.
You said konnichiwa
end
```

waitAndAnswer()の戻り値は、Promise であり、Promise の resolve が呼ばれない限り、then メソッドは呼ばれません。ちなみに NOTE: の行に書いていますが、この行を単純に ` waitAndAnswer("konnichiwa"); にしてしまうと、return 句がないので、最終行を実行した時点で、おそらく暗黙に `return;` が実行された結果、それがPromise にラップされて戻されるので、結果はこうなりました。

```
step 1
Wait for 3 second.
You said hi
step 2
Wait for 3 second.
end
You said konnichiwa
```

# async/await による書き換え

前回のブログで、Promise の resolve と reject 及び、then と catch メソッドの関係がわかってきたところでさらに async/await に進みます。これは、C# をやられている方にはとてもおなじみの非同期処理のキーワードです。

これらのキーワードで上記のプログラムを書き換えていきます。

```javascript
function waitAndAnswer(message:string) : Promise<any> {
    console.log("Wait for 3 second.");
    return new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log(`You said ${message}`);
        resolve();
    }, 3000);
    });
}

async function exec() {
    console.log("step 1");
    await waitAndAnswer("hi");
    console.log("step 2");
    await waitAndAnswer("konnichiwa");
    console.log("end");    
}

exec();
```
実行結果

```
step 1
Wait for 3 second.
You said hi
step 2
Wait for 3 second.
You said konnichiwa
end
```

とこのように、とっても簡単になりました。await 句を、メソッド呼び出しの前に記述すると、結果が戻ってくるのを待ちます。ただ、await 句は、async キーワードで非同期と宣言したメソッドの中にしか書けませんので、メソッドを分けて、async キーワードを追加した、`exec()` メソッドを作成し、最下行で、`exec()`を呼んだ時点で別スレッドとして、exec()が起動されます。exec()メソッド実行のスレッドの内部から、さらにWaitAndAnswerのメソッドが別スレッドで実行されますが、await 句があることで、処理が返ってくるまで待ち合わせます。これはどういう仕組みでしょうか？

# async/await の動作原理

上記のコードは、下記の javascript ファイルに変換されます。

```javascript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
function waitAndAnswers(message) {
    console.log("Wait for 3 second.");
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log(`You said ${message}`);
            resolve();
        }, 3000);
    });
}
function exec() {
    return __awaiter(this, void 0, void 0, function* () {
        console.log("step 1");
        yield waitAndAnswers("hi");
        console.log("step 2");
        yield waitAndAnswers("konnichiwa");
        console.log("end");
    });
}
exec();
```

なんかごちゃごちゃしててよーわからんので、最初の`__awaiter`を自分でもある程度分かるように書き換えて単純にしてみた。

```javascript
"use strict";

var awaiter = function(thisArg, generator) {
    return new Promise((resolve, reject) => {
        let gen = generator.apply(thisArg);
        function fulfilled(value) { try { step(gen.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(gen["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new Promise(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step(gen.next());
    });
}

function waitAndAnswers(message) {
    console.log("Wait for 3 second.");
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log(`You said ${message}`);
            resolve();
        }, 3000);
    });
}
function exec() {
    return awaiter(this, function* () {
        console.log("step 1");
        yield waitAndAnswers("hi");
        console.log("step 2");
        yield waitAndAnswers("konnichiwa");
        console.log("end");
    });
}
exec();
```
実行結果

```
step 1
Wait for 3 second.
You said hi
step 2
Wait for 3 second.
You said konnichiwa
end
```
これは、Generator と、Promise の合わせ技になっている。基本的な構造は、await キーワードをつけた非同期実行が、yield によって、ストップする。await がついたメソッドは、Promise を返却するものである必要があり、Promise が返却されるのを期待されている。yield は、Generator の next()が呼ばれると、次の行に制御を移すが、next()メソッドは、Promise の resolve が呼ばれた時、つまり、非同期処理（ここでは、waitAndAnswers)が完了した時点で、呼ばれる。
  だから、非同期処理(waitAndAnswers)の処理が終わるまでは、yeild部分で処理が止まるため、非同期処理であっても、順次実行が行われる。また、本質的にコールバックなので、メインスレッドがブロックされるわけではない。

# 疑問点

ここまでコードを書いたりしておいて、自分の中で未解決の疑問点が２つある。

## なぜ同期型のメソッドは推奨されないのか？

今回の待ち合わせ対象の非同期メソッドは、waitAndAnswersで、単に待っているだけだが、TypeScript だと、fs.writeFileSync()というメソッドがあって、同期処理をしてくれる。この場合、

```javasctipt
async function write() {
             :
      fs.writeFileSync(name, data);
             :
}

write();
```

とするケースと

```javasctipt
async function writeFile(name, data): Promise<any> {
   fs.writeFile(name, data, (err)=> {
       if (err) {
         reject(err);
       } else {
         resolve();
       }
   }
}

async function write() {
             :
      await writeFile(name, data);
             :
}

write();
```
みたいにするのではスレッドの動き的にどう違うんだろう。メインスレッドが、write()のスレッドを起動して、write()を実行して、最初のケースだと同期なので、write()のスレッドはブロックされる。awaitを使ってケースだと、write()のスレッドはブロックされないけど、待ち合わせることになるから。大きな違いを感じない。メインスレッドはいずれにしてもロックされないから。
　これは師匠に確認してわかりました。私は非同期処理はスレッドが立ち上がって実行されると勘違いしていましたが、そうではないようです。

[非同期I/O待ち](https://csharptan.wordpress.com/2011/12/10/%E9%9D%9E%E5%90%8C%E6%9C%9Fio%E5%BE%85%E3%81%A1/)

この記事が最高にわかりやすかったです。非同期処理ごとにスレッドが起動するのではなく、いくつか上がっているスレッドに対して、割り当てられる感じです。そうであるならば、スレッドがロックされてしまうと、大幅なパフォーマンスロスになりますね。

## awaiter の挙動

先のオリジナルの__awaiter で理解できなかった部分としてこのコードがあります。

```
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected);
```
この時点で、既に非同期処理がおわっていれば、resolve 、そうでなければ、新たにPromise のオブジェクトを作ってfullfill と rejected の関数を割り当てる。と理解できるのですが、

```
new P(function (resolve) { resolve(result.value); })
```
これは何をやっているんでしょうか？ これだと、即座にresolve を呼び出す Promise 
のように見えます。IO待ちなどでは、この時点で、result.value は帰ってきていない可能性も高いでしょう。

私も先生に聞いてみますが、もし皆様もお分かりならご教授いただけるとうれしいです。
回答をいただいたらブログをアップデートします。