编写函数

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

消息作为一个名为 msg 的对象传入。根据惯例,它将具有一个 msg.payload 属性,包含消息的主体。

其他节点可以向消息添加自己的属性,这些属性应在它们的文档中描述。

编写一个函数

输入到 Function 节点的代码表示函数的 主体。最简单的函数仅仅返回消息,完全不做更改:

return msg;

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

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

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

var newMsg = { payload: msg.payload.length };
return newMsg;
注意:构建一个新的消息对象将丢失接收消息中的任何消息属性。这将破坏某些流程,例如 HTTP In/Response 流需要在端到端保留 msg.reqmsg.res 属性。通常,Function 节点 应该 返回它们传递的消息对象,并对其属性进行任何更改。

使用 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 包含为函数节点配置的输出数量。

这使得编写可以处理在编辑对话框中设置的可变数量的输出的通用函数成为可能。

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

// 创建一个与输出数量相同长度的数组
const messages = new Array(node.outputCount)
// 选择随机输出编号以发送消息
const chosenOutputIndex = Math.floor(Math.random() * node.outputCount);
// 仅将消息发送到选定输出
messages[chosenOutputIndex] = msg;
// 返回包含所选输出的数组
return messages;

现在,您可以仅通过编辑对话框配置输出数量,而无需对函数本身进行更改。

多条消息

函数可以通过在返回的数组中返回一个消息数组来在一个输出上返回多条消息。当为一个输出返回多条消息时,后续节点将逐个接收消息,按返回顺序处理。

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

var msg1 = { payload:"从输出 1 的第一条消息" };
var msg2 = { payload:"从输出 1 的第二条消息" };
var msg3 = { payload:"从输出 1 的第三条消息" };
var msg4 = { payload:"来自输出 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第一 条消息,但会克隆其余消息。

如果消息包含无法克隆的内容,或者出于性能原因以最小化发送消息的开销,Function 可以通过将 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 节点所需的任何状态。

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

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

在主 Function 开始处理消息之前,如果 On Start 函数需要完成异步工作,则可以返回一个 Promise。在 On Start 函数完成之前到达的任何消息将被排队,并在其准备好时处理。

清理

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

要么通过添加 close 事件处理程序:

node.on('close', function() {
    // 在此清理任何异步代码 - 关闭连接等。
});

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

日志记录事件

如果节点需要记录某些内容到控制台,可以使用以下函数之一:

node.log("发生了一些事情");
node.warn("发生了一些您应该了解的事情");
node.error("哦,不,发生了一些坏事");

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

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

对于更细粒度的日志记录,node.trace()node.debug() 也可用。 如果没有配置记录器捕获这些级别,则将看不到它们。

处理错误

如果函数遇到应停止当前流程的错误,则应返回空值。要在同一选项卡上触发 Catch 节点,函数应调用 node.error,并将原始消息作为第二个参数:

node.error("遇到错误", msg);

存储数据

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

关于 Node-RED 中上下文的更多信息,请参见 这里

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

  • context - 节点的本地上下文
  • flow - 流范围上下文
  • global - 全局范围上下文

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

注意:这些预定义变量是 Function 节点的一个特性。如果您正在创建自定义节点,请查看 创建节点指南,了解如何访问上下文。

访问上下文有两种模式;同步或异步。内置的上下文存储提供这两种模式。一些存储可能仅提供异步访问,并且如果以同步方式访问则会抛出错误。

要从上下文中获取值:

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

要设置值:

flow.set("count", 123);

以下示例维护函数运行过的次数计数:

// 如果计数器不存在则将其初始化为 0
var count = context.get('count')||0;
count += 1;
// 将值存回
context.set('count',count);
// 使其成为输出 msg 对象的一部分
msg.count = count;
return msg;

获取/设置多个值

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

// Node-RED 0.19 或更高版本
var values = flow.get(["count", "colour", "temperature"]);
// values[0] 是 'count' 值
// values[1] 是 'colour' 值
// values[2] 是 'temperature' 值
// Node-RED 0.19 或更高版本
flow.set(["count", "colour", "temperature"], [123, "red", "12.5"]);

在这种情况下,任何缺失的值都将设置为 null

异步上下文访问

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

// 获取单个值
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) { ... })

传递给回调的第一个参数 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 对象的一部分
                msg.count = count;
                // 发送消息
                node.send(msg);
            }
        });
    }
});

多个上下文存储

自 0.19 起,可以配置多个上下文存储。例如,可以使用 memoryfile 基于的存储。

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) { ... })

全局上下文

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

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

添加状态

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

node.status({fill:"red",shape:"ring",text:"未连接"});
node.status({fill:"green",shape:"dot",text:"已连接"});
node.status({text:"仅文本状态"});
node.status({});   // 清除状态

有关接受参数的详细信息,请参见 节点状态文档

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

加载附加模块

使用 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 起

可以在设置选项卡上为函数节点设置超时。该值(以秒为单位)是运行时将在引发错误之前允许 Function 节点运行的时间。如果设置为 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 是 preferred 记录方式)
  • util - Node.js util 模块
  • setTimeout/clearTimeout - JavaScript 超时函数。
  • setInterval/clearInterval - JavaScript 间隔函数。

注意:每当 Function 节点被停止或重新部署时,它会自动清除任何未完成的超时或间隔定时器。