2
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?

サブフローとリンクコールの実体

Last updated at Posted at 2023-12-10

リンクコールノードによってNode-REDの使い勝手が大きく向上しました。でも、サブフローって前からありました。もういらないのかな?などと考えてしまいがちですが、大きな違いがありますよ!というのがこの記事の内容です。まずはリンクコールの便利な点とサブフローの注意点をあげて、そのあとに違いを語りたいと思います。

TLDR;

  • リンクコール便利だよね
    • サブフローならここに注意が必要
  • リンクコールの呼び出しはシングルトン
  • サブフローのテンプレートはクラス。フロー中のサブフローはインスタンス
  • おまけ

リンクコールの利点

従来のリンクノードはGOTO文なので、別のフローを呼び出しても、呼び出したところに戻ることはできませんでした。戻るためのリンクノードを書いても1か所にしか飛べないので、処理の共通化ができませんでした。処理の共通化をするにはサブフローが以前から使えましたが、これが何かと使いづらいものでした。
そこで現れたのがリンクコールノードです。link callでlink inのつながったフローを呼び出すと、「link callへ返却」というモードにセットしたlink outノードで戻ってくることができます。サブフローと比べて以下のような使いやすさがあります。

  1. 常にフローのタブが表示されているのですぐに編集できる
    • サブフロー:タブは普段閉じた状態
      • 左のパレットをのダブルクリック、または、フロー中のサブフローを開き「サブフローのテンプレートを編集」を押下する
  2. リンクコールノードを含むフローを読み込んでも、呼び出し先は変わらない
    • サブフロー:同じサブフローや、それを含むフローを読みだすとつい間違って複製してしまい、別のサブフローになる
      *「ノードを参照」押下。サブフローを読み込まないか、「置換」のチェックを入れる
  3. 入口を複数持てる(類似処理の一部共通化が容易)
    • サブフロー:入口は一つなので処理の切り替えはmsgの値を用いないといけない
      • スパゲティになりにくいとも言えるので我慢する
  4. flow変数は実行時のタブのflow変数なので直感的

特に2番目の複製はサブフローの開発中にやりがちだったので、精神的に楽になりました。

この記事では最後に書いたflow変数のほか、ノード固有のcontext変数について説明します。

リンクコール先の実体はただ一つ

それではリンクコールを使ってみましょう。
ファンクションノードでflow変数とcontext変数をインクリメント(+1)します。通常の処理のだと、flow変数はタブの中の変数で、context変数はノードの中の変数です。
以下のような処理をそれぞれ二つのファンクションノードに書いてみます。どちらも変数が違うだけでほとんど同じです。前半で変数がなければ初期値をセットして、後半でインクリメントした値をそれぞれの変数にセットするとともにmsg.payloadに出力しています。

let a = context.get("a");

if (!a) {
    a = 0;
}
a++;
context.set("a", a);

msg.payload = "context:"+a;
return msg;
let a = flow.get("a");

if (!a) {
    a = 0;
}
a++;
flow.set("a", a);

msg.payload = "flow:"+a;
return msg;

それぞれの入ったファンクションノードを以下のように並べます。
flow_base.png

順にインジェクトノードを押すと、以下のようにflow変数は増えていきますが、context変数はそれぞれ1のままです。
output_base.png

ではこれをリンクコールで実現します。インジェクトノードでなくlink inノード、デバッグノードでなくlink outノードのものを作成します。今回は上記のフローと同じタブに置きます。
flow_link.png

link outノードはそのままではlink callに戻れないので、モードを「link callノードへ返却」にします。
flow_out.png

呼び出し側はインジェクトノードとデバッグノードの間にlink callノード入れたものを2セット用意します。link callノードは先ほどのlink inノードを呼び出すようにセットします。
flow_call.png

この状態で追加したインジェクトノードを順にクリックすると、以下のようにflow変数は先ほどの続きで増加、context変数は1から順に増加します。
output_call.png

context変数はlink call用モジュールのファンクションノードが1つなので、それが増加しました。
このようにlink callで呼び出した場合は見た目の通り以下のような動作をします。

  • flow変数は参照時(呼び出された先)のflow変数が用いられます(今回は呼び出し元と同じタブなので続きで増加しましたが、別のタブにある際に同じような動作を期待する場合は、global変数にしてください)
  • context変数も参照時のファンクションノードの変数が用いられます

これはlink call の呼び出し先の実体はただ一つ、つまりシングルトンあるいはstatic関数と考えるとわかりやすいと思います。

サブフローのテンプレートはフローの中で実体化される

link call用フローと同じように、サブフローを作成します。
flow_in_sub.png

サブフローを用いたフローを2つ作成します。
flow_sub.png

この状態で追加したインジェクトノードを順にクリックすると、以下のようにflow変数もcontext変数もそれぞれ1になります。
output_sub.png

このように、サブフローはlink callと見た目は似ていますが、以下のような動作をします。

  • flow変数は実行時の(実体化された)サブフロー内のflow変数が用いられます。フローに配置されたそれぞれのサブフローが独立しています。
  • context変数も実行時の(実体化された)サブフロー内のファンクションノードが持つcontext変数が用いられます。フローに配置されたものそれぞれサブフローのファンクションノードが独立しています。

これはサブフローのテンプレートはクラスで、フロー内でサブフローが実体化されるインスタンスと考えるとわかりやすいと思います。

おまけ

flow変数やcontext変数と同じようなことが通信ノードでも生じます。以下のようにwebsocketノードとTCPノードをサブフローに入れて接続します。
flow_in_tcp.png

すると呼び出された側はプロトコルによって接続数が変わります。
flow_tcp.png

興味のある方はソースをのぞいてみると面白いでしょう。

今回のソース


[{"id":"7e93908f05afdc2e","type":"subflow","name":"websocketとtcp","info":"","category":"","in":[],"out":[],"env":[],"meta":{},"color":"#DDAA99"},{"id":"6a32d3897e828b69","type":"group","z":"7e93908f05afdc2e","name":"websocketとtcp","style":{"fill":"#bfdbef","label":true,"color":"#000000"},"nodes":["91b5c777f92a6c35","294d7c3a5330d436"],"x":114,"y":39,"w":312,"h":142},{"id":"91b5c777f92a6c35","type":"websocket out","z":"7e93908f05afdc2e","g":"6a32d3897e828b69","name":"","server":"","client":"c33b9dcf42bfa061","x":270,"y":80,"wires":[]},{"id":"294d7c3a5330d436","type":"tcp out","z":"7e93908f05afdc2e","g":"6a32d3897e828b69","name":"","host":"localhost","port":"3000","beserver":"client","base64":false,"end":false,"tls":"","x":230,"y":140,"wires":[]},{"id":"c33b9dcf42bfa061","type":"websocket-client","path":"ws://localhost:1880/ws/tes1","tls":"","wholemsg":"false","hb":"0","subprotocol":""}]
2
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
2
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?