Functionノードは、受け取ったメッセージに対してJavaScriptコードを実行することができ、 フローを継続するためにゼロ個以上のメッセージを返します。
メッセージはmsg
と呼ばれる一つのオブジェクトとして渡されます。
このオブジェクトは慣例により、メッセージ本体を含むmsg.payload
プロパティを持っています。
独自のプロパティをメッセージに追加することも可能ですが、 そのことをドキュメントに記載するべきです。
Functionノードに入力されたコードは、関数の本体に相当します。 最も単純な関数は、メッセージを単にそのまま返す場合です。:
return msg;
Functionノードがnull
を返すと、メッセージは渡されずにフローが終了します。
Functionノードは必ずmsgオブジェクトを返します。 数値や文字列を返すとエラーが発生します。
返されるメッセージオブジェクトは受け取ったオブジェクトと同一である必要はありません。 全く新しいオブジェクトを作って返すこともできます。 以下がその例です。:
var newMsg = { payload: msg.payload.length };
return newMsg;
msg.req
と msg.res
プロパティが
最初から最後まで失われないようにする必要があります。
一般論として、Functionノードはどんな変更をプロパティに行っても、
受け取ったメッセージオブジェクトを返すべきです。デバッグを助けるため、 node.warn() はサイドバーに警告を表示します。例えば:
node.warn("my var xyz = " + xyz);
詳細情報は以下のログに関するセクションを確認してください。
Functionノードの編集ダイアログで出力の数を変更することができます。 2つ以上の出力がある場合、複数の出力にメッセージを送るために、 複数のメッセージを含む一つの配列を返すことができます。
これにより、条件に応じて異なる出力にメッセージを送る関数を簡単に書くことができます。
例えば、以下の例はtopicがbanana
なら最初の出力ではなく
2番目の出力に全てを送ります。:
if (msg.topic === "banana") {
return [ null, msg ];
} else {
return [ msg, null ];
}
以下の例は、入力されたメッセージをそのまま最初の出力に渡し、 そのメッセージのpayloadの長さを含むメッセージを2番目の出力に渡します。:
var newMsg = { payload: msg.payload.length };
return [msg, newMsg];
Node-RED 1.3以降
node.outputCount
には、Functionノードに設定された出力の数が含まれます。
これにより、編集ダイアログにセットされた出力数を変数にハンドルして関数を作成することができます。
例えば、もし たとえば、受信メッセージを出力の間でランダムに拡散したい場合は、次のように書きます:
// 出力数と同じ長さの配列を生成
const messages = new Array(node.outputCount)
// メッセージ送信する乱数出力を選択
const chosenOutputIndex = Math.floor(Math.random() * node.outputCount);
// 選択した出力だけにメッセージを送信
messages[chosenOutputIndex] = msg;
// 選択された出力を含む配列を返す
return messages;
関数自体を変更することなく、編集ダイアログの設定のみで出力数を構成できるようになりました。
複数メッセージの配列を含む一つの配列を返すことで、 一つの出力に複数のメッセージを返すことができます。 一つの出力に複数のメッセージを返すと、 後続のノードは返された配列の要素の順番にメッセージを一つずつ受け取ります。
以下の例では、msg1
、msg2
、msg3
は最初の出力に送られます。
msg4
は2番目の出力に送られます。
var msg1 = { payload:"first out of output 1" };
var msg2 = { payload:"second out of output 1" };
var msg3 = { payload:"third out of output 1" };
var msg4 = { payload:"only message from output 2" };
return [ [ msg1, msg2, msg3 ], msg4 ];
以下の例は、受信したpayloadを個別の単語に分け、 それぞれの単語を順次送る一つのメッセージを返します。
var outputMsgs = [];
var words = msg.payload.split(" ");
for (var w in words) {
outputMsgs.push({payload:words[w]});
}
return [ outputMsgs ];
メッセージを送信する前に何らかの非同期処理を行う必要がある場合、 Functionノードの最後で(非同期処理の結果の)メッセージを返すことはできません。
その場合、送信したいメッセージ(複数可)をnode.send()
関数に渡して用いる必要があります。
前のセクションで記述したように、
返却するメッセージは同じ配置にします。
以下がその例です。:
doSomeAsyncWork(msg, function(result) {
msg.payload = result;
node.send(msg);
});
return;
Functionノードはnode.send
に渡されたメッセージオブジェクトを全てクローンします。
これは関数内で再利用されるメッセージオブジェクトが意図しない変更がされないようにするためです。
Node-RED 1.0以前では、Functionノードはnode.send
に渡された最初のメッセージはクローンされませんが、
残りはクローンされます。
Functionノードはnode.send
に渡された第一引数をクローンしないことを、
この関数に第二引数としてfalse
を受け渡すことによって、実行環境に要求できます。
これは、メッセージにクローンできないものが含まれている場合、
またはパフォーマンス上の理由でメッセージ送信のオーバーヘッドを最小限に抑える場合におこなわれます。
node.send(msg,false);
Node-RED 1.0以降
Functionノードはメッセージについて非同期処理をおこなう場合、 メッセージ処理が終了したとき実行環境は自動的に検知します。
これを助けるため、Functionノードでは適切なタイミングでnode.done()
を呼び出すことが推奨されます。
これはランタイムにシステムを介してメッセージを適切に追跡できるようにします。
doSomeAsyncWork(msg, function(result) {
msg.payload = result;
node.send(msg);
node.done();
});
return;
Node-RED 1.1.0以降
1.1.0リリースによって、
Functionノードはノードが起動されたときに実行されるコードを提供する初期化処理
タブを提供しています。
これはFunctionノードが必要とする状態にセットアップするために利用されます。
例えば、 メインの関数が利用するローカルコンテキストに値を初期化します:
if (context.get("counter") === undefined) {
context.set("counter", 0)
}
初期化処理関数は、 メインの関数がメッセージを処理し始める前に非同期ジョブを完了させておく必要がある場合にPromiseを返却できます。 初期化処理関数が完了する前に到達した全てのメッセージはキューに蓄積され、準備ができたら処理されます。
Functionノードの中で非同期のコールバックを用いる場合、 フローが再デプロイされる際に処理中のリクエストや使用中のコネクションなどを後片付けする必要があります。 この後片付けは、2種類の方法で実施できます。
close
イベントハンドラを追加します:
node.on('close', function() {
// コネクションの切断など、全ての非同期コードの後片付けをここで行う
});
もしくは、Node-RED 1.1.0から、
ノードの編集ダイアログに終了処理
タブにコードを追加できるようになりました。
ノードがコンソールに何かをログ出力する必要がある場合は、以下の関数のどれかを使うことができます:
node.log("何かが起きました");
node.warn("知っておくべき何かが起きました");
node.error("なんてこった、何か良くないことが起きました");
コンソール出力がどこに表示されるのかはどのOSを利用しているかとどのようにNode-REDを起動したかに依ります。
コマンドラインを使って起動した場合 - ログはコンソールに出力されます。
システムサービスとして実行した場合、システムログに出力されます。PM2のようにアプリの元で実行した場合、独自のログ表示になります。ラズベリーパイ上のインストールスクリプトは、ログを表示させるnode-red-log
コマンドを追加します。
warn
とerror
メッセージはフローエディタ右側のデバッグタブにも送られます。
より詳細なログには、node.trace()
およびnode.debug()
が利用できます。
これらのレベルを出力するようにログが設定されていない場合、ログには出力されません。
Functionノードが実行中のフローを停止させるべきエラーに遭遇した場合、何も返却するべきではありません。
同じタブのCatchノードをトリガーするために、
元のメッセージを第2引数としてnode.error
を呼ぶべきです。:
node.error("エラー発生", msg);
msg
オブジェクトとは違って、Functionノードは複数回の呼び出しの間で、自分のcontext
オブジェクトの中にデータを保持しておくことができます。
Node-REDのコンテキストについての詳細な情報はこちらで確認することができます。
Functionノードの場合、 コンテキストにアクセスすることができる3つの定義済み変数が存在します。:
context
- ノードスコープのローカルなコンテキストflow
- フロースコープのコンテキストglobal
- グローバルスコープのコンテキスト以下の例ではflow
コンテキストを利用しますが、
context
およびglobal
でも同様に利用できます。
コンテキストへのアクセスには、同期または非同期の2種類のモードがあります。 既存のコンテキストストアは両方のモードを提供しています。いくつかのストアは非同期アクセスのみ提供しており、 同期してアクセスがおこなわれるとエラーを発生させます。
コンテキストから値を取得するには以下のようにします。:
var myCount = flow.get("count");
値を設定するには以下のようにします。:
flow.set("count", 123);
以下の例は関数が何回実行されているか、 その回数を保持します。:
// もしまだカウンタが存在していなければ0に初期化
var count = context.get('count')||0;
count += 1;
// 値を書き戻して保存
context.set('count',count);
// 送信するメッセージの一部にカウントを含める
msg.count = count;
return msg;
Node-RED 0.19以降、複数の値の取得または設定を1回でできるようになっています。:
// Node-RED 0.19以降
var values = flow.get(["count", "colour", "temperature"]);
// values[0]には'count'の値が格納される
// values[1]には'colour'の値が格納される
// values[2]には'temprature'の値が格納される
// Node-RED 0.19以降
flow.set(["count", "colour", "temperature"], [123, "red", "12.5"]);
この方法では、値が見つからない場合にはnull
が設定されます。
コンテキストストアでのデータ保管に非同期なアクセスが必要な場合、
get
およびset
関数はコールバック引数が必須です。
// 単一の値を取得
flow.get("count", function(err, myCount) { ... });
// 複数の値を取得
flow.get(["count", "colour"], function(err, count, colour) { ... })
// 単一の値を設定
flow.set("count", 123, function(err) { ... })
// 複数の値を設定
flow.set(["count", "colour", [123, "red"], function(err) { ... })
コールバック関数に渡される第1引数err
は、
コンテキストにアクセスする際にエラーが発生する場合のみセットします。
先述のカウンタの例を、非同期におこなうと以下のようになります。:
context.get('count', function(err, count) {
if (err) {
node.error(err, msg);
} else {
// もしまだカウンタが存在していなければ0に初期化
count = count || 0;
count += 1;
// 値を書き戻して保存
context.set('count',count, function(err) {
if (err) {
node.error(err, msg);
} else {
// 送信するメッセージの一部にカウントを含める
msg.count = count;
// メッセージの送信
node.send(msg);
}
});
}
});
0.19版では、複数のコンテキストストアに値を格納できます。
例として、memory
およびfile
といったストアの両方を利用することができます。
get
/set
関数は、利用するストアを特定するため、
任意の引数を利用することができます。
// 値の同期取得
var myCount = flow.get("count", storeName);
// 値の非同期取得
flow.get("count", storeName, function(err, myCount) { ... });
// 値の同期設定
flow.set("count", 123, storeName);
// 値の非同期設定
flow.set("count", 123, storeName, function(err) { ... })
globalコンテキストは、Node-RED起動時にオブジェクトを予め格納しておくことができます。
オブジェクトは、settings.jsファイルのfunctionGlobalContext
プロパティで
定義することができます。
このテクニックはFunctionノード内で追加モジュールのロードに 利用することができます。
他のノードが行えるのと同じように、Functionノードもステータス表示を装飾することができます。
ステータスをセットするには、node.status
関数を呼びます。
例えば
node.status({fill:"red",shape:"ring",text:"切断されています"});
node.status({fill:"green",shape:"dot",text:"接続されています"});
node.status({text:"文字だけの状態"});
node.status({}); // 状態をクリアします
指定可能なパラメータの詳細については ノードのステータスを参照してください。
ステータスのどんな変化でも、Statusノードでキャッチできます。
functionGlobalContext
オプションの利用Functionノードに追加モジュールを直接ロードすることはできません。
settings.jsファイルでロードし、
functionGlobalContext
プロパティに追加される必要があります。
例えば、os
組込みモジュールはsettings.jsファイルに以下の設定を追加することによって、
すべてのFunctionノードで利用可能になります。
functionGlobalContext: {
osModule:require('os')
}
この時点で、モジュールはFunctionノード内で以下のように参照できます。
global.get('osModule')
設定ファイルでロードされたモジュールは、設定ファイルと同じディレクトリにインストールされる必要があります。
デフォルトのユーザディレクトリ(~/.node-red
)を利用しているだろうほとんどのユーザは以下のようにインストールを実行します。:
cd ~/.node-red
npm install name_of_3rd_party_module
functionExternalModules
オプションを使うNode-RED 1.3.0以降
settings.js ファイルの functionExternalModules
を true
にセットすることで、Functionノードの
編集ダイアログにはモジュールを追加できるリストが表示されます。
また、ノードのコード内でモジュールを参照するために使用する変数を指定します。
追加するモジュールは、ノードがデプロイされる時に ~/.node-red/externalModules/
に自動的にインストールされます。
Functionノード内では以下のオブジェクトが利用できます。
node
node.id
: Functionノードのid - 0.19で追加node.name
: Functionノードの名前 - 0.19で追加node.outputCount
: Functionノードの出力数をセット - 1.3で追加node.log(..)
: メッセージをログ出力node.warn(..)
: 警告メッセージをログ出力node.error(..)
: エラーメッセージをログ出力node.debug(..)
: デバッグメッセージをログ出力node.trace(..)
: トレースメッセージをログ出力node.on(..)
: イベントハンドラーを登録node.status(..)
: ノードステータスを更新node.send(..)
: メッセージを非同期に送信node.done(..)
: 終了時のメッセージcontext
context.get(..)
: ノードスコープコンテキストからプロパティ値を取得するcontext.set(..)
: ノードスコープコンテキストにプロパティ値を設定するcontext.keys(..)
: すべてのノードスコープコンテキストのプロパティキーの一覧を取得するcontext.flow
: flow
と同義ですcontext.global
: global
と同義ですflow
flow.get(..)
: フロースコープコンテキストからプロパティ値を取得するflow.set(..)
: フロースコープコンテキストにプロパティ値を設定するflow.keys(..)
: すべてのフロースコープコンテキストのプロパティキーの一覧を取得するglobal
global.get(..)
: グローバルスコープコンテキストからプロパティ値を取得するglobal.set(..)
: グローバルスコープコンテキストにプロパティ値を設定するglobal.keys(..)
: すべてのグローバルスコープコンテキストのプロパティキーの一覧を取得するRED
RED.util.cloneMessage(..)
: メッセージオブジェクトを再利用できるように安全にクローンしますenv
env.get(..)
: 環境変数を取得するFunctionノードは以下のモジュールと関数を利用可能です。:
Buffer
- Node.jsのBuffer
モジュールconsole
- Node.jsのconsole
モジュール(ログ出力にはnode.log
を利用する方が良いです。)util
- Node.jsのutil
モジュールsetTimeout/clearTimeout
- javascriptのtimeout関数setInterval/clearInterval
- javascriptのinterval関数Note: Functionノードは、停止もしくは再デプロイ時に、 全ての未完了のタイムアウトやインターバルタイマーを自動的にクリアします。
Node-RED: Low-code programming for event-driven applications.
Copyright OpenJS Foundation and Node-RED contributors. All rights reserved. The OpenJS Foundation has registered trademarks and uses trademarks. For a list of trademarks of the OpenJS Foundation, please see our Trademark Policy and Trademark List. Trademarks and logos not indicated on the list of OpenJS Foundation trademarks are trademarks™ or registered® trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.
The OpenJS Foundation | Terms of Use | Privacy Policy | OpenJS Foundation Bylaws | Trademark Policy | Trademark List | Cookie Policy