share facebook facebook twitter menu hatena pocket slack

2017.05.17 WED

Async Functions はなぜ良いのか

WRITTEN BY 山口 与力

忙しい人向け

読みやすいから。

JavaScriptは如何にしてAsync/Awaitを獲得したのかもぜひ読みましょう。

ひまな人向け

Chrome は55から、 Node.js は7.6 からデフォルトで Async Functions (async/await) をサポートしました。

これは明らかに世界平和に近づく出来事です。

サンプル問題

Async Functions は事前に回数が決まっている非同期処理も書きやすくなりますが、真価を発揮するのは何回実行するか実行時までわからない非同期処理です。

今回は例として、(最大の試行回数、試行間の待機時間(ms)、実際の処理の Promise)を引数として受け取り、 Promise を返すというプログラムを考えてみます。

  • 最大試行回数は10回
  • 試行間の待機時間は100 ms
  • 処理の内容は
    • ランダムで 0〜100 の数字を生成する
    • 95 以上なら成功(resolve)
    • それ未満なら失敗(reject)、やり直す

というものにします。つまり、結果が

次はうまくやるでしょう: 93
次はうまくやるでしょう: 46
次はうまくやるでしょう: 34
次はうまくやるでしょう: 37
できました: 96

だったり

次はうまくやるでしょう: 79
次はうまくやるでしょう: 91
次はうまくやるでしょう: 17
次はうまくやるでしょう: 92
次はうまくやるでしょう: 46
次はうまくやるでしょう: 27
次はうまくやるでしょう: 83
次はうまくやるでしょう: 5
次はうまくやるでしょう: 88
できませんでした: 14

だったりするやつです。

実装例: Promise

retryPromise.js

const retryWithPromise = (maxRetries, retryInterval, fn) => {
  return new Promise((rootResolve, rootReject) => {
    const f = (tries, maxRetries, retryInterval, fn, parentResolve, parentReject) => {
      return new Promise((resolve, reject) => {
        fn().then(r => parentResolve(r)).catch(e => {
          if (tries >= maxRetries) return parentReject(e);

          console.log(`次はうまくやるでしょう: ${e}`);

          setTimeout(() => f(tries + 1, maxRetries, retryInterval, fn, parentResolve, parentReject), retryInterval);
        });
      });
    };

    f(1, maxRetries, retryInterval, fn, rootResolve, rootReject);
  });
};

const test = () => new Promise((resolve, reject) => {
  const result = Math.round(Math.random() * 100);

  if (result >= 95) {
    resolve(result);
  } else {
    reject(result);
  }
});

retryWithPromise(10, 100, test)
.then(r => console.log(`できました: ${r}`))
.catch(e => console.log(`できませんでした: ${e}`));

実装例: Async Functions

retryAsync.js

const retryAsync = async (maxRetries, retryInterval, fn) => {
  for (let tries = 1; tries  <= maxRetries; tries++) {
    try {
      return await fn();
    } catch (e) {
      if (tries >= maxRetries) throw e;
      console.log(`次はうまくやるでしょう: ${e}`);
      await new Promise(r => setTimeout(() => r(), retryInterval));
    }
  }
};

const test = async () => {
  const result = Math.round(Math.random() * 100);

  if (result >= 95) {
    return result;
  } else {
    throw result;
  }
};

retryAsync(10, 100, test)
.then(r => console.log(`できました: ${r}`))
.catch(e => console.log(`できませんでした: ${e}`));

まとめ

一目瞭然ですね。もし前者の方が理解しやすい人がいたら手を挙げてください。

環境がネイティブで対応してなくても Babel や TypeScript を使って async/await の庇護と恩恵を受け、完璧に幸福になりましょう!

元記事はこちら

Async Functions はなぜ良いのか

山口 与力

最近うどんばかり食べてる蕎麦県民

cloudpack

cloudpackは、Amazon EC2やAmazon S3をはじめとするAWSの各種プロダクトを利用する際の、導入・設計から運用保守を含んだフルマネージドのサービスを提供し、バックアップや24時間365日の監視/障害対応、技術的な問い合わせに対するサポートなどを行っております。
AWS上のインフラ構築およびAWSを活用したシステム開発など、案件のご相談はcloudpack.jpよりご連絡ください。