これは何?
非エンジニア向けにGoogleAppsScript(GAS)レクチャーを行なっています。プログラムが全くの初めての人を対象にしています。このレクチャーを卒業し、基本的なことが理解できるようになった人が「その次に知っておいたほうがいいこと」の一つにErrorへの対応があるなと思いました。これを説明してみよう、という記事。
対象者
- GoogleAppsScriptおよびJavaScriptでの基本構文は知っているレベル
- 具体的には ケーススタディでしっかり身につく! Google Apps Script超入門 を修了しているレベル
エラーとは?
初学者にとってエラーは可能なら見たくないし、メッセージも英語だから読みたくないし、もうイヤだ。という心境になりますよね?私はなりました。
だけど、エラーは「ここがおかしいよ」って教えてくれているので、それを読み解くことでエラーを解消することができます。
例えば下記の関数を実行するとエラーが出ます。
function myFunction() {
console.log(abc);
}
ReferenceError: abc is not defined
ここでは ReferenceError
というエラーが発生しています。Error - JavaScript | MDN によるとReferenceError
は
不正な参照から参照先の値を取得した時に発生するエラーを表すインスタンスを生成します。
とのことです。
上記のコードでは、宣言されていない abc
という変数をいきなり呼び出していることで「不正な参照から参照先の値を取得」しようとしているために ReferenceError
が起こっています。
(一言で「エラー」と言ってしまいますが、実際は「どういうときにどんなエラーが出るのか」は決められています。SyntaxError
TypeError
あたりはよく出会うエラーかと思います)
実行時エラーが発生すると、新しい Error オブジェクトが生成されスローされます。
とのことです。Errorオブジェクトとは?スローとは?ってなるかもしれませんがここでは「エラーが起こると Errorオブジェクト
という何かができる」くらいに考えておきます。
エラーが発生するとプログラムが終了する
あたりまえ、って思うかもしれませんが、エラーが発生するとプログラムはそこで終了します。例えば下記のコードでは、console.log(abc);
でエラーになるので、それ以降の行は実行されていません。
function myFunction() {
console.log('あいうえお');
console.log(abc);
console.log('かきくけこ');
}
GASで実行したときのログ↓
プログラムが「異常終了」しています。(最後まで実行されていない)
エラーが発生したときに気づきたい
「エラーが発生したことを通知(メールを送るとか、チャットツールに送信するとか)したい」というニーズがあります。めっちゃあります。
「実行したいときに自分で実行するタイプのプログラム」であれば、その処理が終わるまで見ていればエラーが出たことにすぐ気づけると思います。
しかしながら「毎日午前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);
}
}
処理の順番としては下記のようになります。
-
console.log('あいうえお');
が実行される -
const a = b;
でエラーが発生する - エラーが
catch
される -
console.log('エラー発生!', e);
が実行される - プログラムが 「正常終了」 する
これの活用例としては
} catch (e) {
// エラーが発生したことをSlackに通知する処理を書く
}
をしておけば、エラーが出た時点でSlack通知がされ、異常が起こっていることにすぐ気付けます。
また、注目すべき点としては ログでは「実行完了」が出力されています。つまりこれは エラーにはなっていない。つまりプログラムは正常に終了している」 ということです。
try-catch
がない場合はError
が発生するとプログラムが「異常終了」しますが、Error
をcatch
して、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の実行ログを見る画面では下記のように表示されます。
上のログは throw new Error(message);
を消したときのログ。ステータスが「完了」になっています。
下のログはこの一文があるときのログ。こちらは「失敗しました」です。
失敗を失敗として記録しておいたほうが後からわかりやすい、という理由からです。
最後に
JavaScript try-catch
で検索すると、関連する記事がいっぱいできてきます。
-
try-catch
の中にtry-catch
を書くことができる -
try-catch-finally
を使うことでtry
またはcatch
が終了する直前に処理をさせる事ができる
のような情報がありますので、是非調べてみてください! (私はfinally
を使ったことはないです...)
-
正確にはGASでトリガーを設定するときに、「どのタイミングでエラーがあったことの通知をするか」を選ぶことができます ↩