GASで6分を超えて実行するコード

元々書いていた記事の内容だと実行できませんでした。
理由とコードを見たい方は下に残してあるので参照ください。


処理として下記ならいけました。

① 初回実行で毎6分毎に実行させるトリガーを設定
② 必要な処理が終わったらトリガーを削除

```js
/**
 * 経過時間を取得するクラス
 * 
 */
class Time {
  constructor() {
    this.start = new Date();
  }

  getElapsedTime() {
    const now = new Date();
    const s = (now - this.start) / 1000; //ミリ秒から秒数へ変更
    return s;
  }
}

/**
 * 初回処理
 * 
 * @param {string} input
 * @return {object} 説明
 */
function getFirstData() {

  return 
}


function main() {
  
  // Timeクラスをインスタンス化して処理の開始時間を取得
  const time = new Time();

  const shtName = '';
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sht = ss.getSheetByName(shtName);
  const lastRow = sht.getLastRow();

  const prop = PropertiesService.getScriptProperties();
  let handle_data = JSON.parse(prop.getProperty('key')) || 0;


  // 途中までの処理をスプレッドシートに格納するための配列を用意
  const ary = [];


  // 初回の場合の処理
  if (!handle_data) {
    // 初回で処理したい内容の配列をまとめて取得しておく
    handle_data = getFirstData();

    // 6分毎に実行するトリガーを設定   
    ScriptApp.newTrigger('main').timeBased().everyMinutes(6).create()
  }


  // handle_dataで処理したい配列を取得している想定
  while (handle_data.length){
    // shiftで処理するデータを取得して減らす
    const data = handle_data.shift();

    // 処理する内容
    ary.push([]); // 途中までの情報をスプレッドシートに格納するのに二次元配列にする

    // 経過時間の取得
    const elaspedTime = time.getElapsedTime();
    // console.log('elaspedTime' + elaspedTime);

    // 270秒超えたら処理を終了させる
    if (elaspedTime  > 270) {

      // 配列を文字列にしてプロパティストアに格納
      prop.setProperty('key', JSON.stringify(folderIds));

      // ここまでのデータをスプレッドシートに格納
      sht.getRange(lastRow+1, 1, ary.length, ary[0].length).setValues(ary);

      return;
    }
  }

  // 全て終了したらトリガーを削除
  deleteTrigger();

  // 全て終了したら次は回最初から実行されるようにプロパティストアは削除する
  prop.deleteProperty('key');

  // console.log(ary);
  sht.getRange(lastRow+1, 1, ary.length, ary[0].length).setValues(ary);
}

/**
 * トリガーの削除
 */
function deleteTrigger() {
  const triggers = ScriptApp.getProjectTriggers();
  for(const trigger of triggers){
    if(trigger.getHandlerFunction() == "main"){
      ScriptApp.deleteTrigger(trigger);
    }
  }
}
```

多分、これで動くはずです。




## 以前のコードで実行できない理由

理由は下記の箇所
```js
ScriptApp.newTrigger('main').timeBased().after(20).create();
```

トリガーで実行された際にトリガーを仕込むのはできなようになっているみたいです。
2回目の実行のトリガーはこのようになります。

![2021-07-29_22h15_16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428239/da666f5b-cf6c-d4b2-a743-b101461dea15.png)


## 前の記事

6分を超えて実行させるコードを初めて作ったので記録しておきます。
他にもたくさん先人たちの参考記事がありますが、コードをちゃんと読み込む気になれなかったので、
自分で作ったものを汎用的に使いやすい形に変更しました。

前提としてはコンテナバインドでの実行で途中経過はスプレッドシートに格納していきます。
多分、他の人がみたらわかりづらいと思います。

今回の処理手順は下記です。

1. 処理時間を図るために開始時間を取得
1. プロパティストアに処理途中のデータがあるか確認
    1. ない場合は初回処理で処理したいデータを配列で最初に取得する
    1. 存在する場合は上記の処理はスキップ
1. 2.で取得した処理したい内容を順番に実行して、毎回の処理終了時に経過時間を確認
    1. 270秒超えていたら、実行終了
        1. プロパティストアに残りの処理したい内容を格納
        1. ここまで取得してきた二次元配列の内容をスプレッドシートに格納
    1. 270秒超えずに処理が終了
        1. 最終処理として次回実行時にプロパティストアの影響を受けないように削除
        1. ここまでに取得したデータをスプレッドシートに格納
           

```js
/**
 * 経過時間を取得するクラス
 * 
 */
class Time {
  constructor() {
    this.start = new Date();
  }

  getElapsedTime() {
    const now = new Date();
    const s = (now - this.start) / 1000; //ミリ秒から秒数へ変更
    return s;
  }
}

/**
 * 初回処理
 * 
 * @param {string} input
 * @return {object} 説明
 */
function getFirstData() {

  return 
}


function main() {
  
  // Timeクラスをインスタンス化して処理の開始時間を取得
  const time = new Time();

  const shtName = '';
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sht = ss.getSheetByName(shtName);
  const lastRow = sht.getLastRow();

  const prop = PropertiesService.getScriptProperties();
  let handle_data = JSON.parse(prop.getProperty('key')) || 0;


  // 途中までの処理をスプレッドシートに格納するための配列を用意
  const ary = [];


  // 初回の場合の処理
  if (!handle_data) {
    // 初回で処理したい内容の配列をまとめて取得しておく
    handle_data = getFirstData();
  }


  // handle_dataで処理したい配列を取得している想定
  while (handle_data.length){
    // shiftで処理するデータを取得して減らす
    const data = handle_data.shift();

    // 処理する内容
    ary.push([]); // 途中までの情報をスプレッドシートに格納するのに二次元配列にする

    // 経過時間の取得
    const elaspedTime = time.getElapsedTime();
    // console.log('elaspedTime' + elaspedTime);

    // 270秒超えたら処理を終了させる
    if (elaspedTime  > 270) {

      // 配列を文字列にしてプロパティストアに格納
      prop.setProperty('key', JSON.stringify(folderIds));

      // ここまでのデータをスプレッドシートに格納
      sht.getRange(lastRow+1, 1, ary.length, ary[0].length).setValues(ary);

      // 20ms後にmainの再実行するトリガーを仕込む      
      ScriptApp.newTrigger('main').timeBased().after(20).create();
      return;
    }
  }

  // 全て終了したら次は回最初から実行されるようにプロパティストアは削除する
  prop.deleteProperty('key');

  // console.log(ary);
  sht.getRange(lastRow+1, 1, ary.length, ary[0].length).setValues(ary);
}