🤯

no-return-await、return-await、結局どっちなの?

2022/12/22に公開

js/tsを書いてるとasync/awaitを使わない日はないといっていいぐらい使ってると思います。今日はasync/awaitにまつわるモヤモヤについて話したいと思います。

発端

return-await

    const foo = async () => {
      return await someAsync();
    }

no-return-await

    const foo = async () => {
      return someAsync();
    }

どちらも同じ挙動です。ただno-return-awaitはすごく気持ち悪い。asyncはreturnするものをpromiseでラップして返すので、promiseをpromiseでラップしてという感じになります。であれば以下のように素直に通常関数として定義するのがいいのではとモヤモヤが始まりました。

no-async-return-promise

    const foo = () => {
      return someAsync();
    }

return-await、no-return-awaitってなにが違うの

これについてはいろいろ記事(関連記事参照)がありますが、要するに

  1. 処理実行順の違い (return-awaitの方が早い)
  2. stacktraceの詳細度 (return-awaitの方が詳細)

です。ただ混乱するところで、この早くて詳細なreturn-awaitを否定するeslint/no-return-awaitルールには以下記載されています。

When Not To Use It
If you do not want the performance benefit of avoiding return await
(return awaitを避けてパフォーマンス(処理速度)を求めてるのでなければ使わなくても良い)

とあり、どうやら、no-return-awaitの方が速いとのことです。以下実際に検証してみました。

ベンチマークテスト(node v18.11.0)

    import { bench } from "vitest";
    
    const asyncReturnAwait = async () => {
      return await Promise.resolve();
    };
    
    const asyncReturnPrmoise = async () => {
      return Promise.resolve();
    };
    
    const returnPromise = () => {
      return Promise.resolve();
    };
    
    const time = 1000;
    
    bench(
      "async return await",
      async () => {
        await asyncReturnAwait();
      },
      { time }
    );
    
    bench(
      "async return promise",
      async () => {
        await asyncReturnPrmoise();
      },
      { time }
    );
    
    bench(
      "return promise",
      async () => {
        await returnPromise();
      },
      { time }
    );
    
     RERUN  bench.ts x8
    
     ✓ bench.ts (3) 7728ms
         name                            hz     min     max    mean     p75     p99    p995    p999     rme  samples
       · async return await    3,909,844.99  0.0001  4.8365  0.0003  0.0003  0.0004  0.0006  0.0013  ±1.61%  3909845   slowest
       · async return promise  4,107,636.65  0.0001  0.6003  0.0002  0.0003  0.0003  0.0004  0.0007  ±0.80%  4107637
       · return promise        4,788,197.94  0.0001  0.7256  0.0002  0.0002  0.0003  0.0003  0.0006  ±0.97%  4788198   fastest
    
    
     BENCH  Summary
    
      return promise - bench.ts >
        1.17x faster than async return promise
        1.22x faster than async return await

結果

eslintのドキュメントで書かれた内容の通りno-return-awaitの方が若干速く、no-async-return-promiseが一番速い素直な結果でした。

結局どうすればいいの

https://github.com/standard/standard/issues/1442

standard/standard#1442 によると憶測ですがno-return-awaitのルールが2016年?にstandard jsに採用されたとかなんとかでno-return-awaitの勢力が大きくなり、2019年にはひと悶着あって2020年に取り消されたことを受け、今はreturn-awaitというところでしょうか。

実態として結論づけるとすると、現状no-async-return-promiseが処理速度面では最速ではあるものの、いずれも微々たる違いであり、stacktraceが詳細なreturn-awaitに倒すのがよく、またそのlinterであるeslint/require-awaittypescript-eslint/require-awaitがいい感じにautofixしてくれるので(no-return-awaitのケースにおいてawaitをつけてくれる)、速度面でno-async-return-promiseに書き直したほうがいいのでは?という余計な迷いが出ず良さそうです。

ただ平穏に日々暮らしたいだけなので、linterにいろいろ任せて言われるがままにコードを書いていたい。その先には職を失う時代になるのでしょうか。まあそんなことは考えずに今はeslintrcを整理し、育てたいと思ってます。

関連記事

https://zenn.dev/azukiazusa/articles/difference-between-return-and-return-await

https://zenn.dev/uhyo/articles/return-await-promise

catallaxy tech blog

Discussion