no-return-await、return-await、結局どっちなの?
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ってなにが違うの
これについてはいろいろ記事(関連記事参照)がありますが、要するに
- 処理実行順の違い (
return-await
の方が早い) - stacktraceの詳細度 (
return-await
の方が詳細)
です。ただ混乱するところで、この早くて詳細なreturn-await
を否定するeslint/no-return-await
ルールには以下記載されています。
When Not To Use It
If you do not want the performance benefit of avoidingreturn 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
が一番速い素直な結果でした。
結局どうすればいいの
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-await
、typescript-eslint/require-await
がいい感じにautofixしてくれるので(no-return-await
のケースにおいてawaitをつけてくれる)、速度面でno-async-return-promise
に書き直したほうがいいのでは?という余計な迷いが出ず良さそうです。
ただ平穏に日々暮らしたいだけなので、linterにいろいろ任せて言われるがままにコードを書いていたい。その先には職を失う時代になるのでしょうか。まあそんなことは考えずに今はeslintrcを整理し、育てたいと思ってます。
関連記事
Discussion