0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

kintoneの変更イベント(change)はPromiseを待たない — asyncハンドラで値が反映されない罠

0
Posted at

kintoneプラグインで「フィールドAを変更したらフィールドBへ値をコピーする」という定番の処理を実装したところ、フィールド制御は動くのに値のコピーだけが画面に反映されないという症状に遭遇しました。原因はkintoneイベントの仕様にあります。同じ罠を踏む人が多そうなので、切り分け方と修正パターンをまとめます。

症状

kintone.events.on(['app.record.create.change.start_date',
                   'app.record.edit.change.start_date'], async (event) => {
  if (!(await someAsyncCheck())) return event;   // ← ここが罠
  event.record.end_date.value = event.record.start_date.value;
  return event;
});

このコード、someAsyncCheck() を挟んだ途端に end_date への反映が効かなくなります。JSエラーは出ません。黙って効かないだけです。

原因: change イベントは戻り値の Promise を待たない

kintoneのイベントハンドラは、多くのイベント(submitindex.show など)で Promise を返すと解決を待ってくれます。ところが create.change.* / edit.change.*(フィールド値変更イベント)は例外で、Promise を待ちません

ハンドラを async にすると、関数は即座に pending 状態の Promise を返します。kintoneはそれを待たずに変更前の event.record を使って描画を確定させるため、await の後で event.record を書き換えても手遅れ、というわけです。

切り分けのポイント: 「制御系は動くのに値だけ効かない」

厄介なのは、同じ async ハンドラ内でも挙動が割れることです。

処理 asyncハンドラ内での挙動
event.record[x].value = ...(戻り値経由の反映) 効かない(戻り値のPromiseが無視されるため)
kintone.app.record.setFieldShown() など命令的API 効く(呼んだ瞬間に副作用が走るため)

「表示/非表示の切り替えは動いてるから、イベント自体は発火してるはず。なのに値コピーだけ死ぬ」——この切り分けになったら、まずこの仕様を疑ってください。

修正パターン

パターン1: ハンドラを同期に保つ(推奨)

非同期にしていた理由が「キャッシュ済みの値の確認」程度なら、同期読みに書き換えるのが最も確実です。

kintone.events.on([...changeEvents], (event) => {   // asyncを外す
  if (someCheckSync() === false) return event;       // キャッシュを同期参照
  event.record.end_date.value = event.record.start_date.value;
  return event;
});

私たちのケースでは、ライセンス判定のfetch結果を毎回 await していたのを、事前に取得してキャッシュした値を同期参照する形(未取得時は素通し)に変更して解決しました。

パターン2: 非同期処理の結果は kintone.app.record.set() で書く

外部APIの取得結果を使うなどどうしても非同期が必要な場合は、ハンドラ自体は同期で終わらせ、取得完了後に kintone.app.record.set() で画面へ書き込みます。戻り値経由ではなく命令的APIなら、changeイベントの外からでも反映されます。

kintone.events.on([...changeEvents], (event) => {
  fetchSomething(event.record.zip.value).then((addr) => {
    const rec = kintone.app.record.get();
    rec.record.address.value = addr;
    kintone.app.record.set(rec);
  });
  return event;   // ハンドラは同期で即返す
});

テストの落とし穴

もうひとつ実務的な注意を。自前のモックハーネスでイベントハンドラを await fn(event) と呼んでいると、このバグはテストをすり抜けます(モックはPromiseを待ってしまうので、実機で起きない反映が起きる)。私たちはモック側に「changeハンドラが thenable を返していないか」の検査を足して回帰を防ぐようにしました。

まとめ

  • create.change.* / edit.change.*ハンドラのPromiseを待たない(submit等とは挙動が違う)
  • asyncハンドラで await 後に event.record を書き換えても画面に反映されない(エラーも出ない)
  • 命令的API(setFieldShown等)は効くため「制御は動くのに値だけ効かない」切り分けになる
  • 対処は「ハンドラの同期化」または「kintone.app.record.set() への書き込み」

筆者はkintoneプラグイン配布サービス Plumeru を開発しています。この記事は自社プラグインの不具合調査(値の連動が編集画面で効かない)で確認した内容です。

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?