GASのサイドバーで進行状況を表示させる

GAS、遅いんじゃあああ

概要
====

カスタムメニューからスクリプト実行できるようにしたのはいいが、遅いわ、中でも何してるかわからんわ、つらい、、、タイムアウトを待つだけの日々は嫌なんや。
進行状況を表示したいが、特にUI用意されてないやんけ。
出せるのは、、、サイドバーだけか。。。

サイドバーで表示するページを作る
====

まずはテンプレートのHTMLを適当に書き、適当に変数をセットして表示できるようにする

```javascript
function showProgressSidebar(action, steps, finished){
  var template = HtmlService.createTemplateFromFile('Progress')
  template.title = "hogefuga"
  template.action = action
  template.steps = steps
  template.finished = finished === undefined ? false : finished
  SpreadsheetApp.getUi().showSidebar( template.evaluate() )
}
```


```html
<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <H1><?= title ?></H1>
    <HR>
    <H3><?= action ?></H3>
    <br>
    <br>
    
    実行状況：<br>
    <? for (var i = 0; i < steps.length; i++) { ?>
      <? var step = steps[i]; ?>
      <div clas="step <?= step.status ?>">
    <? if( step.status == 'done' ){ ?>
      <span class='mark done'>✓</span>
    <? }else if( step.status == 'exec' ){ ?>
      <span class='mark exec'>◇</span>
    <? }else{ ?>
      <span class='mark stay'>□</span>
    <? } ?>
      <span class="title"><?= step.title ?></span>
      <span class="memo"><?= step.memo ?></span>
      </div>
    <? } ?>
    
    <br>
    
    <? if( finished ){ ?>
    実行完了しました<br>
    <br>
    <center><input type='button' value='閉じる' onClick='google.script.host.close();'></center>
    <? }else{ ?>
    <marquee>実行中です</marquee>
    <? } ?>
  </body>
</html>
```


状況を保持して良きに計らうクラスを作る
====
上のをいちいち呼ぶの面倒臭いので、状態を保持しながらラップしてくれるクラスを作る。

```javascript
// show step progress in sidebar

StepStatus = {
  Done:'done', //did finish
  Exec:'exec', //doing now
  Stay:'stay', //yet stay
}

function StepProgress(title, memo, status){
  'use strict';
  this.title = title
  this.memo = memo === undefined ? "" : memo
  this.status = status === undefined ? 'stay' : status
}

function StepGroup(title, steps){
  'use strict';
  this.title = title
  this.steps = steps
  this.current = -1
  this.times = []
}

StepGroup.prototype.show = function(){
  showProgressSidebar(this.title, this.steps, this.isFinished())
}

StepGroup.prototype.start = function(){ this.step() }
StepGroup.prototype.end = function(){
  if(this.current < this.steps.length){ this.step() }
}

StepGroup.prototype.update = function(memo){
  this.steps[ this.current ].memo = memo
  this.show()
}
StepGroup.prototype.isFinished = function(){
  return this.current >= this.steps.length;
}

StepGroup.prototype.step = function (memo){
  if(this.current>=0){
    this.steps[this.current].status = StepStatus.Done
    if(memo != undefined ){
      this.steps[ this.current ].memo = memo
    }
  }
  this.times.push( new Date().getTime() )
  this.current += 1
  if(this.current < this.steps.length){
    this.steps[this.current].status = StepStatus.Exec
  }
  this.show()
}
```

使う
====
usage

```javascript

function hogefuga( piyo, poyo){
  var Steps = new StepGroup("この操作は["+piyo+"]", [
    new StepProgress("約束された作業"),
    new StepProgress("続いて行う作業"),
    new StepProgress("後はおまけの作業"),
  ])
  Steps.start()
  
  //一つ目の作業
  ...
  Steps.update("途中経過を報告")
  
  ...
  Steps.update("まだまだやってる")
  
  ...
  Steps.step("完了: "+(xxx)+"を処理")
  
  //二つ目の作業
  ...
  Steps.update("完了")
  
  ...
  Steps.step("完了: "+(xxx)+"を処理")
  
  //三つ目の作業
  Steps.step("完了: "+(xxx)+"をおまけ")
  
}
```

まとめ
====

- GASの許されたインターフェイスはカスタムメニューとサイドバーのみ
- サイドバーに表示したページと相互通信するのクソ面倒いやんけ
- 画面書き捨てで出しときゃええやろ。非同期感あるし。

時間オーバーしないように最適化かけてくと、どうしても中で今何やってんのかわからなくなるんで
こういうことすると使ってる時のストレス減って良いですよね並感。