编写函数

Function节点允许针对传递通过它的消息运行JavaScript代码。

消息以名为msg的对象形式传入。按照惯例,它将包含一个msg.payload属性,其中存储着消息的主体内容。

其他节点可能会将自己的属性附加到消息中,这些属性应在各自的文档中进行说明。

编写函数

在Function节点中输入的代码代表该函数的主体部分。 最简单的函数实现就是原样返回消息:

return msg;

如果函数返回null,则不会传递任何消息,流程结束。

函数必须始终返回一个msg对象。返回数字或字符串将导致错误。

返回的消息对象不需要与传入的对象相同;该函数可以在返回之前构造一个全新的对象。例如:

var newMsg = { payload: msg.payload.length };
return newMsg;
Note : constructing a new message object will lose any message properties of the received message. This will break some flows, for example the HTTP In/Response flow requires the msg.req and msg.res properties to be preserved end-to-end. In general, function nodes should return the message object they were passed having made any changes to its properties.

使用node.warn()在侧边栏显示警告信息以帮助调试。例如:

node.warn("my var xyz = " + xyz);

更多详情请参阅下方的日志记录部分。

多路输出

函数编辑对话框允许更改输出数量。如果有多个输出,函数可以返回一个消息数组发送到各个输出端口。

这样可以轻松编写一个根据某些条件将消息发送到不同输出的函数。例如,以下函数会将主题为banana的任何内容发送到第二个输出而非第一个:

if (msg.topic === "banana") {
   return [ null, msg ];
} else {
   return [ msg, null ];
}

以下示例将原始消息原样传递到第一个输出,并将包含负载长度的消息传递到第二个输出:

var newMsg = { payload: msg.payload.length };
return [msg, newMsg];

处理任意数量的输出

自 Node-RED 1.3 起

node.outputCount 包含为函数节点配置的输出数量。

这样就可以编写能够处理从编辑对话框中设置的变量数量输出的通用函数。

例如,如果您希望将传入消息随机分配到多个输出之间,您可以:

// Create an array same length as there are outputs
const messages = new Array(node.outputCount)
// Choose random output number to send the message to
const chosenOutputIndex = Math.floor(Math.random() * node.outputCount);
// Send the message only to chosen output
messages[chosenOutputIndex] = msg;
// Return the array containing chosen output
return messages;

现在您可以直接通过编辑对话框配置输出数量,无需修改函数本身。

多条消息

一个函数可以通过返回消息数组的方式在单个输出上返回多条消息。当某个输出返回多条消息时,后续节点将按照消息被返回的顺序逐一接收这些消息。

在以下示例中,msg1msg2msg3将被发送到第一个输出端口。 msg4将被发送到第二个输出端口。

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 ];

以下示例将接收到的有效载荷拆分为单个单词,并为每个单词返回一条消息。

var outputMsgs = [];
var words = msg.payload.split(" ");
for (var w in words) {
    outputMsgs.push({payload:words[w]});
}
return [ outputMsgs ];

异步发送消息

如果函数在发送消息前需要执行异步操作,则无法在函数结束时返回消息。

相反,它必须使用node.send()函数,传入要发送的消息。它接受与可以返回的消息相同的排列方式,如前面章节所述。

例如:

doSomeAsyncWork(msg, function(result) {
    msg.payload = result;
    node.send(msg);
});
return;

Function节点会克隆您传递给node.send的每个消息对象,以确保在函数中重复使用的消息对象不会被意外修改。在Node-RED 1.0之前,Function节点不会克隆传递给node.send第一个消息,但会克隆其余消息。

该函数可以通过将false作为第二个参数传入,要求运行时不要克隆传递给node.send的第一条消息。当消息包含不可克隆的内容时,或者出于性能考虑需要最小化消息发送开销时,可以采用这种方式:

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节点提供了一个On Start标签页(1.3.0之前标记为Setup),您可以在其中编写节点启动时运行的代码。这可用于设置Function节点所需的任何状态。

例如,它可以在本地上下文中初始化主函数将使用的值:

if (context.get("counter") === undefined) {
    context.set("counter", 0)
}

如果On Start函数需要完成异步工作后才能开始处理消息,它可以返回一个Promise。在On Start函数完成之前到达的任何消息都将被排队,并在准备就绪时处理。

整理

如果在函数中使用异步回调代码,那么每当流程被重新部署时,可能需要清理任何未完成的请求或关闭任何连接。您可以通过两种不同的方式实现这一点。

可以通过添加一个close事件处理程序来实现:

node.on('close', function() {
    // tidy up any async code here - shutdown connections and so on.
});

或者,从Node-RED 1.1.0版本开始,您可以在节点的编辑对话框中的On Stop选项卡(之前标记为Close)中添加代码。

记录事件

如果节点需要向控制台输出日志信息,可以使用以下函数之一:

node.log("Something happened");
node.warn("Something happened you should know about");
node.error("Oh no, something bad happened");

控制台输出的显示位置取决于您的操作系统以及如何启动Node-RED。 如果通过命令行启动 - 日志将显示在该控制台中。如果作为系统服务运行,则可能出现在系统日志里。如果在PM2等应用程序下运行,它将有自己的日志显示方式。在树莓派上,安装脚本添加了node-red-log命令来显示日志。

warnerror 消息也会发送到流程编辑器右侧的调试选项卡。

如需更精细的日志记录,还可使用node.trace()node.debug()。 如果未配置记录器来捕获这些级别,则不会显示这些日志。

错误处理

如果函数遇到需要中止当前流程的错误,它应该不返回任何内容。要在同一标签页触发Catch节点,函数应调用node.error并将原始消息作为第二个参数:

node.error("hit an error", msg);

存储数据

除了msg对象外,该函数还可以在上下文存储中保存数据。

有关Node-RED中上下文的更多信息,请参阅此处

在Function节点中,有三个预定义变量可用于访问上下文:

  • context - 节点的本地上下文
  • flow - 流程作用域上下文
  • global - 全局作用域上下文

以下示例使用flow上下文,但同样适用于contextglobal

Note : these predefined variables are a feature of the Function node. If you are creating a custom node, check the 创建节点指南 for how to access context.

访问上下文有两种模式:同步或异步。 内置的上下文存储提供这两种模式。某些存储可能仅支持异步访问,如果尝试同步访问则会抛出错误。

从上下文中获取一个值:

var myCount = flow.get("count");

设置一个值:

flow.set("count", 123);

以下示例记录了该函数已运行的次数:

// initialise the counter to 0 if it doesn't exist already
var count = context.get('count')||0;
count += 1;
// store the value back
context.set('count',count);
// make it part of the outgoing msg object
msg.count = count;
return msg;

获取/设置多个值

自Node-RED 0.19版本起,还可以一次性获取或设置多个值:

// Node-RED 0.19 or later
var values = flow.get(["count", "colour", "temperature"]);
// values[0] is the 'count' value
// values[1] is the 'colour' value
// values[2] is the 'temperature' value
// Node-RED 0.19 or later
flow.set(["count", "colour", "temperature"], [123, "red", "12.5"]);

在这种情况下,任何缺失值都会被设为null

异步上下文访问

如果上下文存储需要异步访问,getset函数需要额外的回调参数。

// Get single value
flow.get("count", function(err, myCount) { ... });

// Get multiple values
flow.get(["count", "colour"], function(err, count, colour) { ... })

// Set single value
flow.set("count", 123, function(err) { ... })

// Set multiple values
flow.set(["count", "colour"], [123, "red"], function(err) { ... })

传递给回调函数的第一个参数err,仅在访问上下文发生错误时被设置。

计数示例的异步版本变为:

context.get('count', function(err, count) {
    if (err) {
        node.error(err, msg);
    } else {
        // initialise the counter to 0 if it doesn't exist already
        count = count || 0;
        count += 1;
        // store the value back
        context.set('count',count, function(err) {
            if (err) {
                node.error(err, msg);
            } else {
                // make it part of the outgoing msg object
                msg.count = count;
                // send the message
                node.send(msg);
            }
        });
    }
});

多上下文存储

从0.19版本开始,可以配置多个上下文存储。例如,可以同时使用基于memoryfile的存储。

get/set上下文函数接受一个可选参数来标识要使用的存储。

// Get value - sync
var myCount = flow.get("count", storeName);

// Get value - async
flow.get("count", storeName, function(err, myCount) { ... });

// Set value - sync
flow.set("count", 123, storeName);

// Set value - async
flow.set("count", 123, storeName, function(err) { ... })

全局上下文

Node-RED启动时,全局上下文可以预先填充对象。这需要在主settings.js文件中的functionGlobalContext属性下进行定义。

这可用于在Function节点中加载额外模块

添加状态

功能节点也可以像其他节点一样提供自己的状态装饰。要设置状态,可以调用node.status函数。例如

node.status({fill:"red",shape:"ring",text:"disconnected"});
node.status({fill:"green",shape:"dot",text:"connected"});
node.status({text:"Just text status"});
node.status({});   // to clear the status

有关可接受参数的详细信息,请参阅 Node Status documentation

任何状态更新也可以通过状态节点捕获。

加载额外模块

使用functionGlobalContext选项

无法直接在Function节点中加载额外的节点模块。必须在您的settings.js文件中加载,并添加到functionGlobalContext属性中。

例如,通过将以下内容添加到您的settings.js文件中,内置的os模块就可以对所有函数可用。

functionGlobalContext: {
    osModule:require('os')
}

此时,该模块可以在函数中通过 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/node_modules/目录下。

处理超时

自 Node-RED 3.1.0 起

可以在设置选项卡中为函数节点设置超时时间。该值以秒为单位,表示运行时允许函数节点执行多长时间后触发错误。如果设置为0(默认值),则不应用超时限制。


API参考文档

以下对象在Function节点中可用。

node

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超时函数。
  • setInterval/clearInterval - JavaScript的定时器函数。

注意:每当函数节点停止或重新部署时,它会自动清除所有未完成的超时或间隔计时器。