Reactive Web App/Mobileでは、Client Action内でJavaScript要素を配置することで、JavaScriptのコードを記述できる。
では、そのJavaScript要素内で例外が発生して、JavaScript要素外に波及したとき(JavaScript要素内で処理されなかったとき)どうなるかを確認してみる。
環境
Personal Environment(Version 11.17.0 (Build 36291))
Service Studio(Version 11.53.17)
サンプル
Forgeコンポーネント: HousesoftSampleReactiveのV1.0.12。
モジュール > MainFlow > Exception Screenの「③JavaScript要素で発生した例外の処理」下にある各ボタン
結果のまとめ
- JavaScript要素内で発生した例外が未処理だと、ローコード部分(Action Flow)ではAll Exceptionsで処理することになる
- よって、JavaScriptで発生する例外処理をGlobal Exception Handlerに任せるのでなければJavaScript内で行うことを考える。その際、JavaScript APIのLogger.errorでService Centerのエラーログに記録できる
- JavaScriptから非同期のClient Actionを呼んだときは、その内部で例外が起きるとAll ExceptionsでもHandleできないため、thenの第2引数(onRejectedコールバック関数)またはcatchで適切に処理する
1. JavaScript要素で発生した例外をcatchしなかった場合
まず基本を確認すると、Client Action内にJavaScriptを書くには、Action Flow内に要素を配置する。
配置した要素をダブルクリックするとエディタが開くので、JavaScriptのコードを記述する。
ここでは、JavaScript要素でcatchされない例外を発生させたいので、以下のように記述してみる。
throw("throw an exception from inside of JavaScript element: test");
このとき、ローコード部分では、All ExceptionsのException HandlerでHandleできる。
2. JavaScript要素で発生した例外をOutSystemsのエラーログに記録するには
OutSystemsにはエラーログの仕組みが組み込みで備わっている。JavaScriptで発生したエラーについても同じ仕組みで記録したい。
この目的には、JavaScript API - LoggerのBuilt-in Functionであるerrorが使える。
この関数に渡したメッセージは、console.logとService Center両方に出力される。
例外をcatchしたら、Logger.errorを呼べば良い。
try {
throw("throw an exception from inside of JavaScript element: test");
} catch (ex) {
$public.Logger.error($public.ApplicationContext.getCurrentContext().moduleName,
"Logger.errorで出力", ex);
}
3. 非同期のJavaScriptから例外
JavaScript内で\$resolve/$rejectという関数を呼んだ場合、そのClient Actionは非同期の扱いになる。
詳細はOutsystemsで非同期処理を実装する方法(Promiseを使う方法)を参照。
ここでは、以下のようにJavaScript要素の直後にMessage要素、また同じAction Flow内に各種Exceptionに対するHandlerを配置し、①JavaScript要素後も処理が継続するか②throwした例外がローコードでHandleされるか を確認する。
3.1 非同期JavaScriptから例外をthrow
JavaScriptのコード。コード中に\$resolve呼び出しがあるので非同期の扱いになるが、\$resolveの手前に例外のthrowがあるので実行は例外で中断する。
$parameters.Out1 = "正常終了";
throw("throw an exception from inside of JavaScript element: test");
$resolve();
この場合、All ExceptionsのException Handlerで捕捉できる。
3.2 thenのコールバックから例外をthrowした場合
別の非同期Client ActionをJavaScriptで呼び($actions.を経由)、そのコールバック(then関数の第1引数で指定)中でthrowを使って例外を発生させた場合。
以下のサンプルコードでは、thenの第2引数は実際には到達しない。
$actions.AsynchronousClientAction()で呼び出しているAsynchronousClientActionは、非同期のJavaScript要素を配置したClient Action呼び出し。
$actions.AsynchronousClientAction().then(function(result) {
$parameters.Out1 = "正常終了";
throw("throw an exception from inside of then callback: test");
$resolve();
}, function (error) {
throw("rethrow: " + error);
});
$parameters.Out1 = "正常終了";
$resolve();
この場合、ローコードのException Handlerには引っかからない。
3.3 非同期でreject()されるClient Action呼び出しをし、適切なエラー処理コールバックを提供しない場合
3.2と同じ方法で別のClient Actionを読んでいるが、こちらでは、呼び出したClient Action内のJavaScriptが\$reject()呼び出しで終わっている。
\$reject()は、非同期のJavaScriptが失敗で終わったことを示す呼び出し。本来であれば、then関数の第2引数に設定するコールバック関数か、thenにつなげるcatch関数に設定するコールバック関数で処理する。この例では、あえてどちらも設定せず、どうなるか確認している。
$actions.AsynchronousClientActionWithReject().then(function(result) {
$parameters.Out1 = result;
$resolve();
} );
$parameters.Out1 = "Rejected";
$reject();
結果としては、ローコード部分のException Handlerには引っかからなかった。
3.4 非同期でreject()されるClient Action呼び出しをし、thenの第2引数で例外を発生させる
今度は、thenの第2引数(onRejectedコールバック関数)を提供し、その中で例外を発生させている。
$actions.AsynchronousClientActionWithReject().then(function(result) {
$parameters.Out1 = result;
$resolve();
}, function (error) {
throw("rethrow from the second callback of then: " + error);
});
同じく、ローコード部分のException Handlerには引っかからなかった。
3.5 非同期でreject()の代わりに例外throwで終わるClient Action呼び出しをし、thenの第2引数で例外を発生させる
$actions.AsynchronousClientActionWithException().then(function(result) {
$parameters.Out1 = result;
$resolve();
}, function (error) {
throw("rethrow from the second callback of then: " + error);
});
$parameters.Out1 = "正常終了";
throw("throw an exception from inside of asynchronous JavaScript in a Client Action: test");
$resolve();
このパターンも、ローコード部分のException Handlerには引っかからなかった。
確認結果から
以上の結果から、「非同期JavaScriptを呼ぶときは、thenの第2引数(onRejectedコールバック)かcatchを設定する」ことが必要(ローコード部分のException Handlerでは処理できないため)。
ドキュメント(Call an Asynchronous Client Action)に記載されていた以下の記述とも符合する。
You should always include error handling code when calling asynchronous client actions in JavaScript using one of the two approaches presented above: "then(onFullfilled, onRejected)" or "catch(onRejected)".