🕐

Dartの非同期処理でthenを使うな

2024/06/06に公開

株式会社Sally新人エンジニアの @wellPicker です。
日々の業務の中で学んだことを書き残すことで読者の皆様(と将来の自分)の役に立てればと思います。

Dartの非同期処理でthenを使うな

早速本題に入ります。
ついさっき新人であることをアピールしていたにもかかわらず、なかなか過激な言い草です。
ここで本当に言いたいことは、Dartの非同期処理でthenを使うよりもasync/awaitを使った方が良いということです。

async/awaitとthenの違い

そもそもasync/awaitとthenとは何か、それぞれの処理内容とその違いを確認します。

  • async/await
    asyncは関数が非同期であることを表すキーワードです。
    非同期な処理の中でawaitを宣言した場合、awaitの直後に書かれた処理が完了するのを待ってから、それ以降に書かれた処理を実行します。
    下の関数は、呼び出されてから1秒後にprintが実行されます。
Future<void> printTest() async {
  await Future.delayed(Duration(seconds: 1));
  print('1 seconds elapsed');
}
  • then
    thenの直前に書かれた処理が完了するのを待ってから、thenの直後に書かれた処理を実行します。
    下の関数も先ほどと同様に、呼び出されてから1秒後にprintが実行されます。
Future<void> printTest() {
  return Future.delayed(Duration(seconds: 1)).then((_) {
    print('1 seconds elapsed');
  });
}

上記の説明からわかるように、async/awaitとthenでできることは基本的には同じです。
なんなら公式のドキュメントにも「async/awaitを使うならthenは使わなくていいぞ!」的なことが書いてあったので、本当に違いはないと言っても差し支えないでしょう。

thenではなくasync/awaitを使うべき理由

しかし、コードの保守性という点においてasync/awaitとthenの間には大きな違いがあります。
先ほど例示した二つのコードを比較してもらえればわかる通り、基本的にはthenを使うよりもasync/awaitを使った方がネストを一段減らすことができます。
コードのネストは深ければ深いほど可読性が下がるため、同じ内容であればネストが少ない書き方をした方が良いでしょう。

また、thenとasync/awaitのエラーハンドリングの違いについても注目すべきでしょう。
thenの場合、エラーハンドリングにはcatchError構文を使用します。

testProcess().then((value) {
    // 成功時の処理
}).catchError((err) {
    // 失敗時の処理
});

複数の非同期処理がある場合、それぞれに対してcatchErrorを書く必要があります。
一方で、async/awaitの場合は、エラーハンドリングにtry/catch構文を使用できます。
そのため、複数の非同期処理に対して下の例のようにまとめてエラーハンドリングが可能というメリットがります。

    try {
      await testProcess1();
      await testProcess2();
      await testProcess3();
    } catch (e) {
      // 失敗時の処理
    }

それと、複数の連続した非同期処理を書く場合もthenを使うよりasync/awaitを使った方が読みやすいことが多いと思います。

Future<void> test() async {
  await Future.delayed(Duration(seconds: 1));
  await Future.delayed(Duration(seconds: 1));
  await Future.delayed(Duration(seconds: 1));
  await Future.delayed(Duration(seconds: 1));
  await Future.delayed(Duration(seconds: 1));
  print('5 seconds elapsed');
}
Future<void> test() async {
  Future.delayed(Duration(seconds: 1)).then(
    (_) => Future.delayed(Duration(seconds: 1)).then(
      (_) => Future.delayed(Duration(seconds: 1)).then(
        (_) => Future.delayed(Duration(seconds: 1)).then(
          (_) => Future.delayed(Duration(seconds: 1)).then(
            (_) => print('5 seconds elapsed'),
          ),
        ),
      ),
    ),
  );
}

とても極端な例ですが、どちらが読みやすいかは一目瞭然ではないでしょうか?

まとめ:thenを使うよりもasync/awaitを使え

async/awaitとthenの違いを解説した記事なんて世の中に腐るほどあると思いますが、ほとんどの記事はタイトルがそのまま「async/awaitとthenの違いとは?」という感じで中身を読まないと違いが分からない印象を受けました。
しかし結局async/awaitとthenの処理内容に違いはなく、上述した理由から基本的にはasync/awaitの方がthenよりも優れていると言えます。
そのことをパッと思い出せるようにしたかったので、今回はこのようなタイトルで記事を書きました。

現在弊社ではエンジニアを絶賛募集中です!少しでも興味を持っていただけた方は以下を確認していただけると幸いです。

UZU テックブログ

Discussion