Google Apps Scriptに限ったことではありませんが、コーディングミスや、その他様々な理由によってエラーが発生することがあります。
例えば、Forなどによる繰り返し処理の途中でエラーが発生してしまうと、処理が中断されてしまい困ることがあります。
また、利用者に理解できないメッセージが表示されてしまうなど、不安にさせてしまうこともあります。
この記事では、その対処方法として、エラー発生時の例外処理の書き方を説明します。
try...catch
文を使えば、エラー発生時の処理を記述できるthrow new Error("メッセージ")
で独自の例外を発生させられる- HtmlServiceでGAS側のエラーを扱うには
withFailureHandler
を使う
解説用サンプルツール
この記事でサンプルとして利用するツールについて説明します。
今回のツールは、指定したシートのA1セルの値を取得するツールです。
- メインシートのA列に、対象のシート名を入力する
- スクリプトエディタから myFunction関数 を実行する
- メインシートのB列に、対象シートのA1セルの値が転記される
画面イメージ(スプレッドシート)は以下のとおりです。
実行するスクリプトは下記のとおりです。(この時点では例外処理はしていません)
const ss = SpreadsheetApp.getActiveSpreadsheet();
function myFunction() {
const mainSheet = ss.getSheetByName("メイン");
const lastRow = mainSheet.getLastRow();
for (let i = 2; i <= lastRow; i++) {
const sheetName = mainSheet.getRange(i, 1).getValue();
// 対象シートのA1セルの値を取得
const a1Value = getA1Value(sheetName);
// B列に値を転記
mainSheet.getRange(i, 2).setValue(a1Value);
}
}
function getA1Value(sheetName) {
const sheet = ss.getSheetByName(sheetName);
return sheet.getRange("A1").getValue();
}
myFunction関数を実行すると、下図のように、B列に結果が入力されます。
このスクリプトでは、存在しないシート名を指定するとエラーが発生するはずです。
A列(シート名)に無いシートと入力してスクリプトを実行してみます。
無いシートというシートが存在しないため、getRangeメソッドがエラーとなって処理が中断されました。
try…catch文で例外対応
エラーが発生しても処理を継続するためには、try…catch文を使用します。
修正したコードは下記のとおりです。
const ss = SpreadsheetApp.getActiveSpreadsheet();
function myFunction() {
const mainSheet = ss.getSheetByName("メイン");
const lastRow = mainSheet.getLastRow();
for (let i = 2; i <= lastRow; i++) {
const sheetName = mainSheet.getRange(i, 1).getValue();
try {
// 対象シートのA1セルの値を取得
const a1Value = getA1Value(sheetName);
// B列に値を転記
mainSheet.getRange(i, 2).setValue(a1Value);
} catch(error) {
// B列にエラー内容を書き込む
mainSheet.getRange(i, 2).setValue(error.name + ": " + error.message);
}
}
}
function getA1Value(sheetName) {
const sheet = ss.getSheetByName(sheetName);
return sheet.getRange("A1").getValue();
}
try { … }の処理を実行していき、途中でエラーが発生した場合は、try { … }内の処理はそこで中断し、catch(error) { … }の処理を実行します。
無いシートを指定したときの流れは、下記のような感じです。
- 行10: getA1Value関数を呼ぶ
- 行22: getRangeでエラーになる
- tryブロックの行11以降は実行されず、catchブロックの処理が行われる
- 行15: 受け取った例外オブジェクト(error)の内容をセルに書き込む
このコードを実行した後のスプレッドシートはこんな感じです。
エラー発生時の例外処理(エラー内容の書き込み)ができていて、それ以降の行も処理されたことが確認できました。
try…catch文の詳細が知りたい場合は、こちらのドキュメントが参考になります。
必ず実行する処理はfinallyブロックへ
tryブロックとcatchブロックの他に、finallyブロックがあります。
finallyブロックは、例外が発生してもしなくても実行されます。
サンプルスクリプトにおいては、B列に結果を書き込むという処理は、例外が発生してもしなくても行う処理でしたので、コードを修正してfinallyブロックに入れてみました。
const ss = SpreadsheetApp.getActiveSpreadsheet();
function myFunction() {
const mainSheet = ss.getSheetByName("メイン");
const lastRow = mainSheet.getLastRow();
for (let i = 2; i <= lastRow; i++) {
const sheetName = mainSheet.getRange(i, 1).getValue();
let result;
try {
// 対象シートのA1セルの値を取得
result = getA1Value(sheetName);
} catch(error) {
// エラーメッセージを作成
result = error.name + ": " + error.message;
} finally {
// B列に結果を書き込む
mainSheet.getRange(i, 2).setValue(result)
}
}
}
function getA1Value(sheetName) {
const sheet = ss.getSheetByName(sheetName);
return sheet.getRange("A1").getValue();
}
なお、tryブロック内やcatchブロック内にreturn文やthrow文が存在する場合でも、その直前にfinallyブロックの処理が実行されます。
throw文で独自の例外を発生させる
throw文を使うと、独自の例外を発生させることができます。
例として、秘密というシートを対象にした場合に、独自の例外が発生するようにしてみます。
これまでのスクリプトであれば、秘密シートのA1セルの値である「ZZZ」が記載されるはずですが、スクリプトを修正して、エラー「Error: 秘密です」が記載されるようにしてみます。
const ss = SpreadsheetApp.getActiveSpreadsheet();
function myFunction() {
// ... 省略 ... //
}
function getA1Value(sheetName) {
if (sheetName === "秘密") throw new Error("秘密です!");
const sheet = ss.getSheetByName(sheetName);
return sheet.getRange("A1").getValue();
}
myFunction関数を実行すると、下図のようになりました。
このように、throw new Error("メッセージ")
とすると、その時点で独自の例外を発生させ、catchブロックに制御を移すことができます。
throw文の詳細が知りたい場合は、こちらのドキュメントが参考になります。
HtmlServiceでGAS側のエラーを扱う
HtmlServiceで作成したウェブアプリ等からgoogle.script.runでGoogle Apps Scriptの関数を実行する際には、withFailureHandlerを使うことでGAS側で発生したエラーに対処することができます。
サンプルとして、シート名を入力してボタンを押すと、そのシートのA1セルの値が表示されるウェブアプリを作成しました。
まずは、例外処理なしのスクリプトを作成しました。
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<input type="text" id="sheet-name-input">
<button onclick="handleClickButton()">A1の値を取得</button>
<p id="result-text"></p>
<script>
function handleClickButton() {
const sheetName = document.getElementById("sheet-name-input").value;
const resultText = document.getElementById("result-text");
// GASのgetA1Value関数を実行
google.script.run.withSuccessHandler((result) => {
// GAS側の処理が正常終了したら、結果を画面に表示する
resultText.textContent = "A1の値は " + result + " です。";
}).getA1Value(sheetName);
}
</script>
</body>
</html>
存在しないシート名「無いシート」を入力してボタンを押すと、エラーが発生し、結果を画面に表示する処理は実行されませんでした。
Html側のスクリプトにwithFailureHandler(…)を追加し、GAS側で例外が発生した場合にエラー内容を表示するようにしてみました。
<!-- ... 省略 ... -->
<script>
function handleClickButton() {
const sheetName = document.getElementById("sheet-name-input").value;
const resultText = document.getElementById("result-text");
// GASのgetA1Value関数を実行
google.script.run.withSuccessHandler((result) => {
// GAS側の処理が正常終了したら、結果を画面に表示する
resultText.textContent = "A1の値は " + result + " です。";
}).withFailureHandler((error) => {
// GAS側の処理がエラーになったら、エラー内容を画面に表示する
resultText.textContent = error.message;
}).getA1Value(sheetName);
}
</script>
</body>
</html>
例外が発生した場合にも処理が停止せず、エラー内容が画面に表示されるようになりました。
GAS側で独自の例外をthrowした場合も、ちゃんと取り扱うことができています。
受け取ったエラーオブジェクトの他のプロパティも表示してみました。
(GAS側で発生したエラーを受け取ると、nameプロパティは「ScriptError」になるようです。)
// ... 省略 ... //
google.script.run.withSuccessHandler((result) => {
resultText.textContent = "A1の値は " + result + " です。";
}).withFailureHandler((error) => {
// GAS側の処理がエラーになったら、エラー内容を画面に表示する
resultText.innerHTML =
`error => ${error}<br />` +
`error.name => ${error.name}<br />` +
`error.message => ${error.message}<br />` +
`error.stack => ${error.stack}`;
}).getA1Value(sheetName);
// ... 省略 ... //
TypeErrorの場合:
サンプルスクリプトの独自のエラーの場合:
google.script.runおよび、withSuccessHandler, withFailureHandlerについての詳細は、こちらのドキュメントが参考になります。
コメント