GASで実行時間5分の壁を超える

こんにちは。テックブログ編集者の萱場です。

GASでは、1つのプログラムの実行時間が5分という制限があります。

その制限を超えるとエラーが帰ってきて強制的にプログラムが止まってしまいます。

数千行か1万行以上の処理を行う場合、場合によっては、5分では終わらない場合もあります。

そんな場合に使えるテクニックを解説いたします。

考え方

以下のように考えてプログラミングしてきます。

  1. プログラミング実行時にその実行開始時間を変数として保持します。
  2. ループ処理の中で、実行開始時間と現在の時間を比較して5分過ぎているか、判定するようにします。
  3. 5分過ぎていなければ、通常の処理を実行。
  4. 5分過ぎていれば、1分後に同じプログラムを動かすトリガーを用意した後で、現在実行中のプログラムを停止します。

上記のような実装をすることによって、5分過ぎているかの判定を自動で行い、過ぎている場合は、新しい、トリガーによる同じプログラムの自動実行を行い、時間内であれば、通常処理を行うというような動きが可能になります。

サンプルコード

function myFunction(){

// プログラム実行時点での時間を取得。
const startTime = new Date();

 for (ループ条件){ 
    
    // 現在の時間と開始時の時間を比較して、その差を分単位に変換
    let diff = parseInt((new Date() - startTime) / (1000 * 60));

    //  もし5分以上、経過していたら
    if(diff >= 5){ 

   // まず過去のトリガーを削除
      delMyFuncTrigger(functionName);

   // 現在時刻を取得して、一分後にトリガーが発動するように設定する。
      let dt = new Date();
      dt.setMinutes(dt.getMinutes() + 1);  //1分後に再実行
      ScriptApp.newTrigger(functionName).timeBased().at(dt).create();
      Logger.log("5分経過。一旦終了します。")
      break;

    } else {

     // 処理を記述

    }
}

// 過去のトリガーを削除する関数
function delMyFuncTrigger(functionName) {
  const triggers = ScriptApp.getProjectTriggers();
  for(let trigger of triggers){
    if(trigger.getHandlerFunction() == functionName ){
      ScriptApp.deleteTrigger(trigger);
    }
  }
}

解説

const startTime = new Date();

上記のコードで、プログラム起動時点での時刻をDateオブジェクトとして、startTime という定数に保存します。

 let diff = parseInt((new Date() - startTime) / (1000 * 60));

new Date() – startTime 部分では、この判定時点での時刻からstartTimeを引くことで、プログラム起動時から、どのくらい経過したかをミリ秒単位で取得します。

それを分単位に変換して、数値型に変換して 次に出てくる条件演算子で使用できるようにします。

5分以上経っていた場合の処理

新しく Dateオブジェクトを作成してdtとして定義します。

dt.setMinutes(dt.getMinutes() + 1);

上記のコードで、dtに1分後の時間をセットします。

 ScriptApp.newTrigger(functionName).timeBased().at(dt).create();

上記のコードで、任意の関数(functionName)を時間主導型トリガーで、dtの時間(つまり今から1分後)に発火する新しいトリガーを作成できます。

トリガーの削除に関して

上記の方法で5分の壁を超えることはできますが、都度新しく設定するトリガーはどんどん過去の無効のトリガーとして溜まっていき、トリガーがたまりすぎると「Too many trigers」といったようなエラーが出て処理が止まってしまいます。トリガーを管理画面からいちいち削除するのもしんどいので、新しいトリガーを作成する前に、過去のトリガーを消す実装もしておくほうが良いと思います。

function delMyFuncTrigger(functionName) {
  const triggers = ScriptApp.getProjectTriggers();
  for(let trigger of TRIGGERS){
    if(trigger.getHandlerFunction() == functionName ){
      ScriptApp.deleteTrigger(trigger);
    }
  }
}

これは、現在設定されているトリガーを取得し、ループで回します。

そして任意の関数と同じ関数を発火させるトリガーがあった場合は、そのトリガー削除するといったコードになっています。

まとめ

いかがだったでしょうか。

これで、GASの5分の壁を超えることができるので、自動化対象の幅もぐっと増えるのではないでしょうか。

株式会社デザインXでは、アパレル業界のお客様に向けて、ECサイトや業務基幹システム(ERP)の開発・導入支援を行っております。ご興味をお持ちの方は、是非下記リンクよりお気軽にお問い合わせください。

また、株式会社デザインXでは、ソフトウェアエンジニア・社内SEなど、共に働く仲間を募集しています。テクノロジーでアパレル業界のBtoBビジネスを変えたいという熱意をお持ちの方、お待ちしております。ご興味のある方は、以下のリンクからお問い合わせください!


投稿日

カテゴリー:

投稿者:

タグ: