大家好,我是 N8N大学 的主编。作为在自动化战场上摸爬滚打 8 年的老兵,我见过太多新手在 n8n 的 Function 节点里折戟沉沙。
很多人觉得 Function 节点是 n8n 的“万能钥匙”,能写 JS 代码就意味着无所不能。但现实是,当你把一段在浏览器里运行完美的代码贴进去,n8n 却给你抛出一串莫名其妙的 Cannot read property 'x' of undefined 时,那种抓狂感,我懂。
今天,笔者不讲空洞的理论,只讲硬核的实战。我会把我在 n8n 里写 JS 代码踩过的所有坑,一个个填平了给你看。
一、你以为的 JS,其实不是 n8n 的 JS
这是新手最容易栽的第一个跟头。在 n8n 的 Function 节点里写代码,千万不要以为你是在写浏览器脚本或者 Node.js 后端。
这里有一个核心概念必须记死:沙箱环境 (Sandbox)。
n8n 为了安全和性能,把 Function 节点的代码运行在一个隔离的沙箱里。这意味着:
- 没有 DOM: 你拿不到
document或window对象。别想在这里操作 DOM,也别想用 jQuery。 - 受限的 API: 并不是所有的 Node.js 原生模块都能直接
require。比如fs(文件系统) 就被禁用了,n8n 不允许你随意读写服务器的文件。 - 全局对象不同: 你只能访问 n8n 提供的特定对象,比如
$input、$json。
二、数据流转的“暗礁”:$input 与 $json
在 n8n 的旧版本中,我们习惯了直接操作 items。但在新版本的 Function 节点(特别是 Function Item 节点)中,数据流的处理方式发生了巨变。
坑点: 很多人在 Function 节点里直接写 let data = items[0].json;,然后发现报错 items is not defined。
避坑指南:
- 理解 $input: 在 Function 节点中,输入数据通过
$input对象访问。如果你要获取 JSON 数据,标准写法是:
const inputData = $input.first().json;
这里 .first() 是指获取当前 Item 的第一个(在 Function Item 模式下)或整个输入流的第一个。如果你处理的是数组,记得遍历。
- 输出数据: 不要试图修改全局变量。你需要显式地调用
$output.send()或者return一个对象。
记住:n8n 的数据是不可变的。你不能直接修改输入,必须创建新的输出。
三、异步编程的陷阱:Promise 的正确姿势
Function 节点虽然支持异步代码,但 n8n 对异步的处理有它自己的逻辑。
坑点: 很多开发者习惯在 JS 里写 async/await,但忘记处理返回值。或者在同步代码里混入异步操作,导致流程提前结束。
实战案例: 假设你需要在 Function 节点里调用一个外部 API(虽然推荐用 HTTP Request 节点,但有时逻辑需要写在 JS 里)。
错误写法:
async function getData() {
const res = await fetch('https://api.example.com');
return res.json();
}
// 这样写,n8n 可能会在你拿到结果前就结束执行
getData();
正确写法: 必须确保 Function 节点知道你在等待异步完成。在 n8n 中,最稳妥的方式是返回一个 Promise 或者使用回调风格(如果 n8n 版本支持)。
但在 n8n 的 Function 节点中,最推荐的异步处理方式是显式地使用 $output。
const axios = require('axios'); // 注意:n8n 可能内置了 axios 或类似库,具体看版本
async function processItem(input) {
try {
const response = await axios.get('https://jsonplaceholder.typicode.com/todos/1');
// 将外部 API 的数据合并到原始数据中
return {
...input,
json: {
...input.json,
externalData: response.data
}
};
} catch (error) {
console.error(error);
return input; // 出错时至少返回原始数据
}
}
// 使用 Promise.all 处理所有输入项
return Promise.all($input.all().map(item => processItem(item)));
这行代码利用了 Promise.all,确保所有异步任务完成后再交给 n8n 处理。
四、调试神技:console.log 之外的利器
在 n8n 里写 JS,最痛苦的莫过于报错信息模糊。你写的 console.log 到底输出到哪了?
坑点: 找不到日志,或者日志太多看花眼。
解决方案:
- 善用 Executions 面板: 这是 n8n 最强的调试工具。点击工作流中的运行记录,你可以看到每一步的输入和输出 JSON。
- 强制报错法: 当你不确定数据结构时,不要盲目写逻辑。先写一句
console.log(JSON.stringify($input.first().json, null, 2));。把结构打印出来,再针对性取值。 - 局部调试: 如果逻辑复杂,不要把一大坨代码塞进 Function 节点。先把逻辑拆分,用 Code 节点(新版 Function 节点)或者多个 Function 节点串联,这样排错范围更小。
五、版本差异:Function vs. Code 节点
这里有一个非常容易混淆的点,尤其是 n8n 从 0.x 升级到 1.x 之后。
旧版 Function 节点: 基于 node.js 的 vm 模块,写法相对自由,但安全性较低,且对 ES6+ 支持有限。
新版 Code 节点(推荐): n8n 推出了基于 QuickJS 的新 Code 节点。它的优势是跨平台(甚至能在浏览器版运行),但它是沙箱环境,且不支持 require 外部模块(除非通过 n8n 的特殊配置)。
笔者的建议:
- 如果你的逻辑只是简单的数据转换(如:
item.json.field = item.json.field.toUpperCase()),优先使用 Code 节点(JavaScript 版)。它更轻量,执行更快。 - 如果你需要用到
moment.js、lodash等第三方库,且你的 n8n 版本支持,或者你必须在 Function 节点里运行,才选择旧版 Function 节点。
注意:在 n8n Cloud 或最新版 Docker 部署中,Code 节点是未来的趋势。
六、遇到报错怎么办?常见错误代码解析
当你看到红色的报错弹窗时,不要慌,看懂报错信息是解决问题的第一步。
1. "Cannot read property 'json' of undefined"
- 原因: 你试图访问一个不存在的 Item。例如,你的输入是空的,或者你使用了错误的索引
items[5]但数组里只有 3 个元素。 - 解决: 检查上游节点是否有输出。在代码开头加一个防御性判断:
if (!$input.first()) return [];
2. "ReferenceError: xxx is not defined"
- 原因: 变量未声明或作用域问题。在新版 Code 节点中,严格模式下不能使用未定义的变量。
- 解决: 检查拼写,或者使用
const声明变量。
3. "Execution timed out"
- 原因: 代码运行时间超过了 n8n 的超时限制(默认可能是 60 秒或更短)。
- 解决: 优化算法,避免死循环。如果必须长时间运行,考虑将任务拆解,或者使用 Wait 节点分批处理。
FAQ:N8N大学 学员常见提问
Q1: 为什么我在 Function 节点里引入第三方库总是失败?
A: 这通常是因为沙箱限制。在 n8n 的 Function 节点中,你需要先在 n8n 的设置里安装该依赖(如果是本地部署),或者确保 n8n 版本支持。在新版 Code 节点中,默认不支持 require 外部库。如果你必须用第三方库,建议使用 HTTP Request 节点调用外部 API,或者在自定义节点开发中引入。
Q2: Function 节点和 Code 节点到底用哪个?
A: 简单的数据处理(字符串、数组、对象操作)用 Code 节点,性能更好。复杂的逻辑或需要特定 Node.js 模块(如加密、文件流)时,用 Function 节点。目前 n8n 官方更推荐 Code 节点作为 JavaScript 的首选。
Q3: 如何处理 Function 节点中的错误,防止整个工作流中断?
A: 使用 try...catch 包裹你的核心逻辑。在 catch 块中,你可以选择返回一个标记了错误的 Item,或者将错误信息写入日志,然后通过 n8n 的 Error Trigger 节点来捕获并发送通知,而不是让整个流程直接红字停止。
总结与资源
在 n8n 中编写 JavaScript 代码是实现高度定制化自动化的必经之路,但它不是写普通的脚本。理解 沙箱环境、掌握 数据流的输入输出、学会利用 Executions 进行调试,是避开那些“坑”的关键。
作为 N8N大学 的主编,我的建议是:不要急于在 Function 节点里写复杂的业务逻辑,先用 n8n 自带的节点(如 Set, Split, Merge, HTTP Request)组合。当发现原生节点无法满足需求时,再引入 Function 节点作为“粘合剂”。
如果你在 n8n 的使用中还有其他搞不定的难题,欢迎随时访问 n8ndx.com,这里有更深入的教程和活跃的社区等着你。