トランザクション

OS7.1.0以降

トランザクションは、応答を待つ問い合わせです。obniz.plugin.send()やcloud.pluginSend()ではデータが一方向に流れるだけですが、トランザクションを使うと、obniz.jsとLuaプラグインが互いに問い合わせて答えを受け取れます。

方向 問い合わせ 応答
obniz.js → Lua obniz.plugin.callWait() Luaスクリプトのreturnの値
Lua → obniz.js cloud.transactionWait() obniz.plugin.onCloudTransactionの返り値

この2つは組み合わせて使えます。callWait()で実行したスクリプトの中から、cloud.transactionWait()でobniz.jsに問い合わせられます。


obniz.plugin.callWait(lua_script, timeout)

端末でLuaスクリプトを実行し、その返り値を待ちます。execLua()はスクリプトを送るだけですが、callWait()はスクリプトがreturnした値を受け取ります。

引数 型 説明
lua_script string 実行するLuaスクリプトです。
timeout number 省略可。結果を待つ時間(ミリ秒)です。デフォルトは30000です。0を指定すると時間の制限なく待ちます。

Promiseを返します。

  • スクリプトがreturnした最初の値を文字列にしてresolveします。数値は文字列に変換されます。テーブル、boolean、nilなどそれ以外の値は空文字列になるため、JSONなどの文字列に変換してから返してください。
  • スクリプトでエラーが起きたとき(Luaのエラーメッセージ付き)、タイムアウトしたとき、端末との接続がリセットされたときはrejectします。

スクリプトはプラグインと同じLuaの環境で動作します。そのため、プラグインのグローバル変数を読み書きしたり、関数を呼んだりできます。

// Luaを実行して返り値を待つ
const result = await obniz.plugin.callWait(`return "hello from lua"`);
console.log(result); // "hello from lua"

// 動作中のプラグインの値を読む
const count = await obniz.plugin.callWait(`return tostring(counter)`);

// Luaでエラーが起きるとPromiseはrejectされる
try {
  await obniz.plugin.callWait(`MUST FAILED`);
} catch (e) {
  console.log("lua error:", e.message);
}

端末で同時に実行できるcallWait()のスクリプトは、cloud.transactionWait()で待っているものも含めて100個までです。それを超えると、Promiseはbusyでrejectされます。


cloud.transactionWait(data, callback, timeout_ms)

Luaプラグインからobniz.jsへデータを送り、応答を待ちます。

引数 型 説明
data string obniz.jsへ送るデータです。
callback function 省略可。応答を受け取ったとき、またはタイムアウトしたときにcallback(success, result)として呼ばれます。
timeout_ms number 省略可。応答を待つ時間(ミリ秒)です。デフォルトは30000です。整数で指定してください。

callbackは省略できます。cloud.transactionWait(data, timeout_ms)と書くこともできます。

返り値 型 説明
success boolean obniz.jsが正常に応答した場合はtrueです。ハンドラがエラーを投げた場合、ハンドラが設定されていない場合、タイムアウトした場合、問い合わせができなかった場合はfalseです。
result string | nil obniz.jsからの応答です。ハンドラがエラーを投げた場合はそのメッセージです。タイムアウトの場合はnilです。

呼び出せる場所

cloud.transactionWait()は、応答が届くまで実行中のLuaコードを一時停止します。これができるのは、obniz.plugin.callWait()で実行したスクリプトの中だけです。

on_online_loop()やon_command()、起動時に読み込まれるプラグインなど、それ以外の場所で呼んだ場合は待たずに、すぐfalse, nilを返します。

あるスクリプトが応答を待っている間も、プラグインのほかの部分は動き続けます。on_online_loop()などのハンドラは引き続き呼ばれます。タイムアウトの後に届いた応答は無視されます。

-- obniz.plugin.callWait()の中で実行する
local success, result = cloud.transactionWait("get_config", 5000)
if success then
  threshold = tonumber(result)
  return "updated"
else
  return "failed"
end

obniz.plugin.onCloudTransaction

obniz.js側でcloud.transactionWait()に応答するハンドラです。

obniz.plugin.onCloudTransaction = async (data, str) => {
  // data: number[] (受信したバイト列), str: 同じデータを文字列にしたもの
  return "reply";
};
  • ハンドラはasync関数にできます。返したPromiseが完了した時点で応答を送ります。
  • 文字列、バイト配列(number[])、Bufferのいずれかを返してください。何も返さない場合は空文字列を送ります。
  • エラーを投げる(またはrejectする)と、Luaにはsuccess = falseが返り、resultにはエラーメッセージが入ります。
  • ハンドラが設定されていない場合、obniz.jsはすぐにsuccess = falseで応答します。そのため、Luaがタイムアウトまで待つことはありません。

Example: 端末からクラウドに問い合わせる

この例では、obniz.jsが端末でスクリプトを実行します。スクリプトはobniz.jsに設定値を問い合わせ、その値を反映して結果を返します。

// プラグインからの問い合わせに答える
obniz.plugin.onCloudTransaction = async (data, str) => {
  if (str === "get_interval") {
    // 例えば、ここでデータベースや別のAPIから値を読み出す
    return "60000";
  }
  throw new Error(`unknown request: ${str}`);
};

// 問い合わせて答えを待つスクリプトを実行する
const result = await obniz.plugin.callWait(`
  local success, value = cloud.transactionWait("get_interval", 5000)
  if not success then
    return "failed: " .. tostring(value)
  end
  interval = tonumber(value)
  return "interval=" .. value
`);
console.log(result); // "interval=60000"