0
0

[GAS, JavaScript] try-catch と Error のこと

Posted at

これは何?

非エンジニア向けにGoogleAppsScript(GAS)レクチャーを行なっています。プログラムが全くの初めての人を対象にしています。このレクチャーを卒業し、基本的なことが理解できるようになった人が「その次に知っておいたほうがいいこと」の一つにErrorへの対応があるなと思いました。これを説明してみよう、という記事。

対象者

エラーとは?

初学者にとってエラーは可能なら見たくないし、メッセージも英語だから読みたくないし、もうイヤだ。という心境になりますよね?私はなりました。

だけど、エラーは「ここがおかしいよ」って教えてくれているので、それを読み解くことでエラーを解消することができます。

例えば下記の関数を実行するとエラーが出ます。

function myFunction() {
  console.log(abc);
}
ReferenceError: abc is not defined

ここでは ReferenceError というエラーが発生しています。Error - JavaScript | MDN によるとReferenceError

不正な参照から参照先の値を取得した時に発生するエラーを表すインスタンスを生成します。

とのことです。

上記のコードでは、宣言されていない abc という変数をいきなり呼び出していることで「不正な参照から参照先の値を取得」しようとしているために ReferenceError が起こっています。

(一言で「エラー」と言ってしまいますが、実際は「どういうときにどんなエラーが出るのか」は決められています。SyntaxError TypeError あたりはよく出会うエラーかと思います)

Error - JavaScript | MDN によると

実行時エラーが発生すると、新しい Error オブジェクトが生成されスローされます。

とのことです。Errorオブジェクトとは?スローとは?ってなるかもしれませんがここでは「エラーが起こると Errorオブジェクト という何かができる」くらいに考えておきます。

エラーが発生するとプログラムが終了する

あたりまえ、って思うかもしれませんが、エラーが発生するとプログラムはそこで終了します。例えば下記のコードでは、const a = b; でエラーになるので、それ以降の行は実行されていません。

function myFunction() {
  console.log('あいうえお');
  console.log(abc);
  console.log('かきくけこ');
}

GASで実行したときのログ↓

image.png

プログラムが「異常終了」しています。(最後まで実行されていない)

エラーが発生したときに気づきたい

「エラーが発生したことを通知(メールを送るとか、チャットツールに送信するとか)したい」というニーズがあります。めっちゃあります。

「実行したいときに自分で実行するタイプのプログラム」であれば、その処理が終わるまで見ていればエラーが出たことにすぐ気づけると思います。

しかしながら「毎日午前0時に定期実行される」のようなプログラム(GoogleAppsScriptでトリガーで定期実行しているようなプログラム)の場合、途中でエラーになっても気づけません。1

私がよく使うのは「定期実行しているプログラムがエラーになったら、Slackのエラー通知チャネルに通知する」ということをよくやります。これを実装するときに try-catchを使います。

try-catch の使い方

構文としては下記のようになります。

try {
  // エラーが起こるかもしれない処理
} catch(e) {
  // エラーが起こったときの処理
}

実際にエラーを起こしてみましょう。

function myFunction() {
  try {
    console.log('あいうえお');
    console.log(abc);
    console.log('かきくけこ');

  } catch (e) { // e は好きな変数名を付けてOKです。eでもerrでもerrorでも。
    console.log(e);
  }
}

GASのログ↓
image.png

処理の順番としては下記のようになります。

  1. console.log('あいうえお'); が実行される
  2. const a = b; でエラーが発生する
  3. エラーがcatchされる
  4. console.log('エラー発生!', e); が実行される
  5. プログラムが 「正常終了」 する

これの活用例としては

  } catch (e) {
    // エラーが発生したことをSlackに通知する処理を書く
  }

をしておけば、エラーが出た時点でSlack通知がされ、異常が起こっていることにすぐ気付けます。

また、注目すべき点としては ログでは「実行完了」が出力されています。つまりこれは エラーにはなっていない。つまりプログラムは正常に終了している」 ということです。

try-catchがない場合はErrorが発生するとプログラムが「異常終了」しますが、Errorcatchして、console.log(e); をするというプログラムが実行され、全体としては「正常にプログラムが実行された」という結果になります。

Errorオブジェクトを自分で作ることもできます

上の例だとconsole.log(abc); が実行されたところで ReferenceError が発生しています。
自分でErrorを作ることもできます。

function myFunction() {
  try {
    console.log('あいうえお');
    throw new Error('自分で作ったエラー');
    console.log('かきくけこ');

  } catch (e) {
    console.log(e);
  }
}
あいうえお
[Error: 自分で作ったエラー]

ここで「スロー (throw)」が出てきましたね。

再掲
実行時エラーが発生すると、新しい Error オブジェクトが生成されスローされます。

今回は自分で実行時エラーを自分で発生させ、新しいErrorオブジェクトを生成(new)し、throwしたわけです。

try-catch の実践例

私はGASをよく使うのでGASの例を書きます。

プログラムの本体はmain()関数です。ただ、この関数を実行したときに何らかのErrorが発生したときに、すぐSlackに通知したい。

/**
 * トリガーで実行するのはこの関数 (もっといい命名あったら教えてほしい)
 */
function do_main() {
  try {
    main(); // この関数でErrorが発生したらcatchしたい。
  } catch (e) {
    const message = `[プロジェクト名] でエラーです。
エラー内容: ${e.stack}`;

    alert(message); // Slackに通知するalert関数は別に定義しあるとする
    throw new Error(message); // これをしておくことで実行ログにエラーとして記録される
  }
}


/**
 * こちらが処理の本体
 */
function main() {
  // スプレッドシートから値をとったり、別サービスのAPIにリクエストしたりする
  // 実行したときにエラーが出る可能性がある
  
  console.log(abc); // ここでエラーを発生させてみる
}

Slackに通知される message は下記のようになります。

[プロジェクト名] でエラーです。
エラー内容: ReferenceError: abc is not defined
    at main (無題:25:15)
    at do_main (無題:6:5)
    at __GS_INTERNAL_top_function_call__.gs:1:8

こうしておくことで、main()が実行されてErrorがあったらSlackに通知されることですぐに気づくことができます。

補足情報としては、

    const message = `[プロジェクト名] でエラーです。
エラー内容: ${e.stack}`;

のように e.stack としています。出力結果をみるとわかるように、こうすることでエラーがどこで起こったかの履歴を出力してくれます。↓ この部分。

    at main (無題:25:15)
    at do_main (無題:6:5)
    at __GS_INTERNAL_top_function_call__.gs:1:8

__GS_INTERNAL_top_function_call__ が実行されて、
do_main が実行されて、
main でエラーが起こってるよ、ということですね。(エラーが起こってる箇所が上になってる)

正常終了させるのか、異常終了させるのか

下記の処理については意見が分かれそうかなと思いました。

  } catch (e) {
    const message = `[プロジェクト名] でエラーです。
エラー内容: ${e.stack}`

    alert(message);
    throw new Error(message);
  }

私は↑こうしておくことで「Slackに通知したうえで、プログラムを異常終了させる」をしています。
最後の一文

    throw new Error(message);

を書くことで「わざわざErrorを発生させて異常終了させてる」んですよね。

この理由ですが、GASの実行ログを見る画面では下記のように表示されます。

image.png

上のログは throw new Error(message); を消したときのログ。ステータスが「完了」になっています。
下のログはこの一文があるときのログ。こちらは「失敗しました」です。

失敗を失敗として記録しておいたほうが後からわかりやすい、という理由からです。

最後に

JavaScript try-catch で検索すると、関連する記事がいっぱいできてきます。

  • try-catch の中に try-catch を書くことができる
  • try-catch-finally を使うことで tryまたはcatchが終了する直前に処理をさせる事ができる

のような情報がありますので、是非調べてみてください! (私はfinallyを使ったことはないです...)

  1. 正確にはGASでトリガーを設定するときに、「どのタイミングでエラーがあったことの通知をするか」を選ぶことができます

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0