Vue で同期的に値を代入できる場面で敢えてasync awaitをしてみるとどうなるか


実験のメモです。

`Vue` のバージョンは`2.6.12` です。

## 実験に使ったコード
実際のコードを見たほうがわかりやすいと思います。

``` vue:QiitaSample.vue
<template>
  <div>
    <button @click="execute">async-awaitなし</button>
    <button @click="executeAsync">async-awaitあり</button>
    <div>
      {{ counter }}
    </div>
  </div>
</template>

<script>
export default {
  name: "QiitaSample",
  data() {
    return {
      counter: 0,
    };
  },
  methods: {
    //---------------
    //async-awaitなし
    execute() {
      this.increment(1);
      this.increment(3);
      this.increment(5);
    },
    increment(c) {
      this.counter += c;
    },
    //---------------
    //async-awaitあり
    async executeAsync() {
      await this.incrementAsync(1);
      await this.incrementAsync(3);
      await this.incrementAsync(5);
    },
    async incrementAsync(c) {
      //このコード自体は同期的に実行できる操作
      this.counter += c;
    },
  },
};
</script>
```

`execute`, `executeAsync` ともに `counter` をインクリメント

- `execute` は `increment` を実行してインクリメント
- `executeAsync` は `incrementAsync` を実行してインクリメントしていますが
`incrementAsync` を `await` するようにしています。(何のために？ というのはなしで😅)

## execute の場合の結果

`vue devtools` と `Chrome` のパフォーマンス計測の結果です。

![qiita02-increment.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628749/fa0a0453-3015-39b0-fbfb-1cb60d0f2a06.jpeg)

![qiita01X-increment .jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628749/fbcbf81b-8c48-e0a9-c71e-67ab5edc9341.jpeg)


`updated` が1回、 `render`, `patch`がそれぞれ1回となりました。




## executeAsync の場合の結果


![qiita04-incrementAsync.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628749/f322435c-63f6-ce59-64d1-4896da347efb.jpeg)


![qiita03X-incrementAsync.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628749/1231c7bd-56d6-b2bb-d977-331d06608a13.jpeg)


`updated` が3回、 `render`, `patch`がそれぞれ3回 になっています。

`Vue` のバージョン3でも試してみましたが 同様に`render`, `patch` が3回実行されていました。

## ソースを見てみる
値を代入した際に [scheduler.jsのqueueWatcher](https://github.com/vuejs/vue/blob/v2.6.12/src/core/observer/scheduler.js#L164)が呼ばれてるようです。(ここに至る過程は省略します。)
ここで更新をスケジューリングしています。

タイミングにもよると思いますが、キューのフラッシュも実行されており
これには`nextTick` が使われています。

[nextTick関数](https://github.com/vuejs/vue/blob/v2.6.12/src/core/util/next-tick.js#L87)のソースを見るとコールバックが蓄積されており、`timerFunc` を呼んだ時に蓄積されたコールバックが実行されるようです。

`timerFunc` は実行される環境によって実装が変わります。
`Chrome` で試したところ Promiseを使った[コチラ](https://github.com/vuejs/vue/blob/v2.6.12/src/core/util/next-tick.js#L44)の実装が使われていました。

`Promise.resolve()` したものに対して`then`を呼んでいるようです。


これが原因で 値の代入→スケジュールした処理が実行→値の代入→スケジュールした処理が実行→値の代入→スケジュールした処理が実行 となったのかな？？ 🤔

間違ってたらすみません。

## 何が言いたいかというと
今回のような同期的に実行できる場面で `async-await` を使う場面はあまりないように思います。(多分..)

おさえておくのは「`nextTick` が`Promise.resolve()` で行われている」 ということです。(もちろん環境によります。)


例えば

``` js
const resp1 = await request1();
this.hoge = resp1.hoge;

if (resp1.isFoo) {
  const resp2 = await request2();
  this.hoge = resp2.hoge
}
```
このように、サーバーへリクエストを行いその結果を `data` にセットするような場面では 2回描画される可能性があります。

``` js
const resp1 = await request1();
if (resp1.isFoo) {
  const resp2 = await request2();
  this.hoge = resp2.hoge
} else {
  this.hoge = resp1.hoge;
}
```
こうしたほうが良いかもしれません。

コンポーネントの更新コストが低い場合は問題にならないかもしれないですが、
コストが高い場合は、注意しておいたほうが良いかもしれません。