GoogleAppsScript(GAS)の自動リトライ処理

# GASでまれに予期しないエラーが起こる
GoogleAppsScript(GAS)は無料で使えて、様々なことができてとても便利です。
ただ、無料で使わせてもらっておいてなんですが、ちょこちょこ予期しないエラーが発生してエラー対応で大変な思いをするときがあります。

たとえば
**スプレッドシートの操作時に「スプレッドシートとの接続が切れました。」とエラーになったが、すぐに再実行してみると普通に成功する。。**
みたいなことが起きます。

リトライすれば成功するのですが、GASには自動でリトライする処理は用意されていないので
以下のように自分で実装していきます。
**・エラーになったら自動でリトライする**
**・トライ回数は自由に設定できる**
**・どんなエラーが起きたか分かるようログは出す**
**・指定回数実行してもエラーになる場合は、通知する**


# 起きたり起きなかったりするエラーを再現
リトライ処理を実装するにあたって、テストしやすいように
起きたり起きなかったりするエラーを再現してみます。

タイムスタンプを取得して、そのタイムスタンプが**偶数だったらエラー**をスローさせます。
```javascript: タイムスタンプが偶数だったらエラーをスロー
function main() {
  // タイムスタンプ取得
  const timestamp = Date.now();
  console.log("タイムスタンプ: " + timestamp);

  // タイムスタンプが偶数だったらエラー(起きたり起きなかったりするエラーの代わり)
  if (timestamp % 2 === 0) {
    throw new Error("テストエラー");
  }

  return "成功！！";
}
```

実際に実行させると
**・偶数でエラー発生**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557347/640af274-8dff-4200-e14a-cf457155969f.png)

**・奇数でエラー発生せず**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557347/627e0faf-374a-1d29-0862-4b2e725ab530.png)

このように、タイムスタンプが偶数ならエラー、奇数なら正常終了します。
これをエラーが起きたり起きなかったりするmainの処理に見立てていきます。

# GASの自動リトライ処理
GASの自動リトライ処理のコードは以下になります。
```const tryCount = 3;```の数値を変えるだけで何回トライさせるかを変更することができます。
エラーの通知のさせ方など適宜変更していただければ。


```javascript: GAS自動リトライ
function retryFunc() {

  // トライ回数の設定
  const tryCount = 3;
  // 指定回数分ループ
  for(let count=1; count < tryCount+1; count++){

    try {
      console.log("トライ回数: " + count);

      // メインの処理を実行
      const result = main();

      // メインが成功したら、処理終了
      console.log(result);
      return;

    // エラーが発生した場合
    } catch (e) {

      // トライ回数分リトライ
      if (count < tryCount) {
        console.warn(e.stack);
        console.warn("予期せぬエラーが発生したためリトライします。");
        continue;
      }

      // 以下、トライ回数実行しても成功しない場合エラー通知
      console.error(e.stack);
      console.error(tryCount + "回トライしても成功しませんでした。");
      // ここに、メールやらSlack通知を入れて通知して処理終了
      console.log("**** エラー通知の代わり ****");

    }
  }
}
```

## 1回で成功した場合
エラーなっていないので```catch```に入らずそのまま```return```で処理が終了します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557347/086391bb-7166-2c73-9a60-ec40b278a77d.png)

## 1回目はエラー、2回目で成功した場合
1回目でエラーになり```catch```に入り```if (count < tryCount)```がcountが ```1```, tryCountが```3```なのでif文に入り```continue```されます。
2回目はエラーにならず```catch```に入らないため```return```で処理が終了します。
「2回目もエラー、3回目で成功」も同様です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557347/65ce01f1-a44b-95b2-bb52-07a5e2f652ff.png)

## 3回ともエラー
1, 2回目でリトライされて、3回目もエラーになった場合は```if (count < tryCount)```がcountが ```3```, tryCountが```3```となり、if文に入らずエラー通知されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557347/9a3b4065-9745-38a4-3056-d917b2c0119f.png)

# コピペ用
ご自身のGASエディタにコピペできるようコード全体をのせます。
retryFunc()を実行して挙動の確認してみてください。

```javascript:
function retryFunc() {

  // トライ回数の設定
  const tryCount = 3;
  // 指定回数分ループ
  for(let count=1; count < tryCount+1; count++){

    try {
      console.log("トライ回数: " + count);

      // メインの処理を実行
      const result = main();

      // メインが成功したら、処理終了
      console.log(result);
      return;

    // エラーが発生した場合
    } catch (e) {

      // トライ回数分リトライ
      if (count < tryCount) {
        console.warn(e.stack);
        console.warn("予期せぬエラーが発生したためリトライします。");
        continue;
      }

      // 以下、トライ回数実行しても成功しない場合エラー通知
      console.error(e.stack);
      console.error(tryCount + "回トライしても成功しませんでした。");
      // ここに、メールやらSlack通知を入れて通知して処理終了
      console.log("**** エラー通知の代わり ****");

    }
  }
}


function main() {
  // タイムスタンプ取得
  const timestamp = Date.now();
  console.log("タイムスタンプ: " + timestamp);

  // タイムスタンプが偶数だったらエラー(起きたり起きなかったりするエラーの代わり)
  if (timestamp % 2 === 0) {
    throw new Error("テストエラー");
  }

  return "成功！！";
}

```