嘿,我是N8N大学的主编。今天不聊宏大的自动化架构,咱们聊点实在的。
如果你和我一样,每天都在 n8n 里摆弄各种 API 数据,那你一定离不开 Function 节点。它就像 n8n 里的瑞士军刀,能处理任何复杂的逻辑。但说实话,这把刀并不好驾驭,尤其是在处理 JSON 数据时。
笔者在过去的 8 年里,用 n8n 搭建了上百个流程,Function 节点里的坑几乎踩了个遍。这篇文章,我把那些半夜调试、抓耳挠腮的教训总结出来,带你一次性避坑。
坑一:把 JSON 当字符串处理,导致逻辑崩溃
这是新手最容易栽的第一个跟头。n8n 的输入数据(Input Data)本质上是 JavaScript 对象,但有时候它看起来像字符串。
场景复现:
你通过 HTTP 请求获取了一个 JSON 响应,想在 Function 节点里取某个字段的值。你直接写了 item.json.field,结果报错:
TypeError: Cannot read property 'field' of undefined
原因分析:
很多时候,API 返回的数据虽然是 JSON 格式,但在 n8n 的某些节点流转中,或者因为 Header 设置问题,它可能被包裹成了 字符串。比如,它看起来是 '{"field": "value"}' 而不是 {"field": "value"}。在 JS 里,字符串是没有属性访问权限的。
解决方案:
在进入复杂逻辑前,先做个类型判断。如果是字符串,先解析它。
// 在 Function 节点里
let rawData = items[0].json;
if (typeof rawData === 'string') {
rawData = JSON.parse(rawData);
}
// 然后再操作
const value = rawData.field;
坑二:JSON 解析报错 "Unexpected token"
这个报错简直是 Function 节点的常客,尤其是处理网页抓取或非标准 API 返回时。
场景复现:
你尝试用 JSON.parse() 去解析一段从网页抓取回来的数据,结果 n8n 无情抛出:
SyntaxError: Unexpected token u in JSON at position 0 或者 Unexpected token <
原因分析:
这通常不是你的代码写错了,而是数据源本身有问题:
- 数据为空: API 返回了
undefined或null,直接传给JSON.parse()就会报错。 - 格式错误: 返回的不是纯 JSON,可能夹杂了 HTML 错误页(比如 502 错误页),或者存在非法的转义字符。
避坑指南:
永远不要假设数据是干净的。在解析前,加上防御性代码:
try {
const data = JSON.parse(yourJsonString);
} catch (error) {
console.log("解析失败,原始数据是:", yourJsonString);
// 这里可以决定是返回空对象还是抛出异常
}
坑三:想修改数据结构,却忘了 return
n8n 的 Function 节点工作方式和普通的 JS 脚本略有不同。它不是在“原地”修改数据,而是依赖你的 return 值。
场景复现:
你想给每个 JSON 对象加一个时间戳字段 timestamp。
// 错误写法
for (let item of items) {
item.json.timestamp = Date.now(); // 你以为你改了
}
// 这里的 items 虽然变了,但 n8n 不知道你要把它传给下一个节点,除非你 return
正确写法:
n8n 的 Function 节点必须 return 一个数组,这个数组会被传递给下一个节点。
const newItems = [];
for (let item of items) {
// 这里的 item 是引用,修改 item.json 会影响原对象,但最好显式处理
item.json.timestamp = Date.now();
// 显式 push 或者直接操作数组
newItems.push(item);
}
// 关键点:必须 return
return newItems;
笔者经验:
为了代码清晰,我习惯使用 map 方法,这样逻辑更直观,也不容易忘记 return。
坑四:多层嵌套 JSON 的“点语法”地狱
处理深层嵌套的 JSON(比如微信公众号或钉钉的复杂回调)时,代码会变得非常丑陋。
场景复现:
你需要取值:data.result.list[0].user.profile.name。
// 脆弱的代码
if (items[0].json.data && items[0].json.data.result && items[0].json.data.result.list) {
const name = items[0].json.data.result.list[0].user.profile.name;
}
原因分析:
只要中间任何一层缺失(比如 list 是空数组,或者 user 字段不存在),你的整个流程就会报错崩溃。
解决方案:使用 ES2020 的可选链操作符 (Optional Chaining)
n8n 的 Function 节点通常运行在较新的 Node.js 环境上,支持 ?. 语法。这是处理 JSON 嵌套的神器。
// 优雅的代码
const name = items[0]?.json?.data?.result?.list?.[0]?.user?.profile?.name ?? '默认名称';
console.log(name); // 如果找不到,就输出 '默认名称',不会报错
坑五:性能杀手——大 JSON 的同步循环
有时候你需要处理成千上万条 JSON 数据。如果你在 Function 节点里写了 for (let i = 0; i < 10000; i++),n8n 的界面可能会卡死,甚至内存溢出。
原因分析:
n8n 的单个 Function 节点是单线程执行的。如果你的 JSON 数据量巨大(比如几 MB 的数组),同步循环会阻塞整个 Node.js 事件循环。
优化方案:
- 分批处理: 如果数据量过大,不要在一个 Function 节点里处理完。建议拆分成多个流程,或者使用 Split Out 节点先拆分数据。
- 使用原生数组方法: 虽然
map、filter也是同步的,但它们通常比手写的for循环在 V8 引擎优化上略好一点点(聊胜于无),更重要的是代码可读性。 - 异步处理: 如果是极其耗时的计算,考虑使用 Code 节点(Node.js 版本),利用其完全的异步能力。
FAQ 问答
Q1:Function 节点和 Code 节点有什么区别?我该用哪个?
笔者建议: 如果是简单的 JSON 字段映射、加减乘除,用 Function 节点(基于旧版 API,适合简单逻辑)。如果你需要调用 npm 包、进行复杂的异步操作或处理大数据流,请切换到 Code 节点(Node.js 环境)。N8N大学 建议新手先从 Function 节点练手。
Q2:为什么我修改了 JSON 里的值,下游节点没收到变化?
这是 n8n 的数据流机制决定的。Function 节点默认处理的是当前节点的 Input Data。如果你修改了 item.json,必须确保 return 修改后的 items 数组。如果使用的是 Set 节点来修改 JSON,记得要在 Output 字段配置里选中 "Keep Only Set",否则可能被覆盖。
Q3:如何在 Function 节点里处理日期时间?
直接使用原生的 new Date() 即可,但在处理不同 API 返回的时间戳(秒或毫秒)时容易出错。记住:JS 的 Date.now() 是毫秒级,很多 API(如 Unix 时间戳)是秒级。转换公式:new Date(timestamp * 1000)。
总结与资源
处理 JSON 数据是 n8n 自动化中最基础也最重要的一环。Function 节点虽然强大,但也需要你对 JavaScript 有基本的敬畏之心。记住类型检查、异常捕获和显式返回这三点,能帮你避开 90% 的坑。
如果你在 n8n 的使用中还有其他棘手的 JSON 处理问题,欢迎在 N8N大学 (n8ndx.com) 留言,笔者会挑选典型问题在下期文章中复盘。
保持学习,保持自动化。