You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

n8n节点开发:.call()调用时同时访问类实例this与thisArgs

问题:n8n节点开发中this上下文冲突问题

我正在开发一个n8n节点,本质是触发特定事件时运行的函数。我创建了一个抽象类供n8n环境调用,但无法调用其方法——因为n8n用Class.execute.call(thisArgs)调用函数,会覆盖类实例的this上下文。

n8n库调用我的类的方式

以下是从n8n源码中复制的调用逻辑:

import { createContext, Script } from 'vm'
import { AbstractNode } from './n8n'

const context = createContext({ require })
export const loadClassInIsolation = <T>(filePath: string, className: string) => {
  const script = new Script(`new (require('${filePath}').${className})()`)
  return script.runInContext(context) as T
}

async function run(): Promise<void> {
  const myClass = loadClassInIsolation<AbstractNode<unknown>>(
    '../dist/codex/node/Codex.node.js',
    'Codex',
  )
  const thisArgs = {
    prepareOutputData: (d: any): any => ({ ...d }),
  }
  console.log(await myClass.execute.call(thisArgs, thisArgs))
}

void run()

我的抽象类

下面是使用this时出问题的抽象类:

import { IExecuteFunctions, INodeExecutionData, INodeType } from 'n8n-workflow'

export abstract class AbstractNode<TParams> implements Omit<INodeType, 'description'> {
  private _executeFunctions: IExecuteFunctions = null

  set executeFunctions(value: IExecuteFunctions) {
    this._executeFunctions = value
  }

  get executeFunctions(): IExecuteFunctions {
    return this._executeFunctions
  }

  abstract run(t: TParams): Promise<INodeExecutionData>

  async execute(): Promise<INodeExecutionData[][]> {
    this.executeFunctions = this as unknown as IExecuteFunctions

    // 此行报错:TypeError: this.run is not a function
    await this.run({ prompts: ['hello', 'world'] } as TParams)

    return this.executeFunctions.prepareOutputData([
      { json: { answer: 'Sample answer' } },
    ])
  }
}

动态实例化的类

这个类实现了AbstractNode的抽象run方法:

import { Logger } from '@nestjs/common'
import { FirefliesContext } from '@src/common'
import { AbstractNode } from '@src/n8n'
import { INodeExecutionData } from 'n8n-workflow'

type CodexParams = { prompts: string[] }

export class Codex extends AbstractNode<CodexParams> {
  run({ prompts }: CodexParams): Promise<INodeExecutionData> {
    console.log(`Prompts="${prompts.join(', ')}"`)
  }
}

已尝试的方案

错误原因是.call(thisArgs)覆盖了execute函数内的this上下文。试过把execute改成箭头函数,但这样就访问不到thisArgs了。

我的需求是:有没有办法同时访问类实例的this和.call()传入的thisArgs?这样既能调用实现的抽象方法,又能使用thisArgs里的辅助函数。


解决方案

方案1:闭包绑定类实例的this

在抽象类构造函数中保存类实例的this到闭包变量,execute内用该变量调用类方法,同时用当前this访问thisArgs:

export abstract class AbstractNode<TParams> implements Omit<INodeType, 'description'> {
  private _executeFunctions: IExecuteFunctions = null
  private readonly _instanceThis: this

  constructor() {
    // 保存类实例的this到闭包
    this._instanceThis = this
  }

  set executeFunctions(value: IExecuteFunctions) {
    this._executeFunctions = value
  }

  get executeFunctions(): IExecuteFunctions {
    return this._executeFunctions
  }

  abstract run(t: TParams): Promise<INodeExecutionData>

  async execute(): Promise<INodeExecutionData[][]> {
    // 当前this是call传入的thisArgs
    this.executeFunctions = this as unknown as IExecuteFunctions

    // 用保存的_instanceThis调用类方法
    await this._instanceThis.run({ prompts: ['hello', 'world'] } as TParams)

    return this.executeFunctions.prepareOutputData([
      { json: { answer: 'Sample answer' } },
    ])
  }
}

方案2:利用调用时的参数传递

观察n8n调用代码,它会把thisArgs作为参数传入execute,因此可以直接通过参数访问thisArgs,同时提前把execute绑定到类实例:

// 修改n8n调用逻辑,先绑定execute到类实例
async function run(): Promise<void> {
  const myClass = loadClassInIsolation<AbstractNode<unknown>>(
    '../dist/codex/node/Codex.node.js',
    'Codex',
  )
  // 绑定execute到类实例,确保this指向类本身
  myClass.execute = myClass.execute.bind(myClass)
  const thisArgs = {
    prepareOutputData: (d: any): any => ({ ...d }),
  }
  // 直接传入thisArgs作为参数
  console.log(await myClass.execute(thisArgs))
}

修改抽象类的execute方法:

async execute(executeContext: IExecuteFunctions): Promise<INodeExecutionData[][]> {
  this.executeFunctions = executeContext

  // 此时this是类实例,可正常调用run
  await this.run({ prompts: ['hello', 'world'] } as TParams)

  return this.executeFunctions.prepareOutputData([
    { json: { answer: 'Sample answer' } },
  ])
}

方案3:用Symbol保存实例引用

通过唯一Symbol给实例添加自身引用,execute内通过该Symbol获取类实例:

// 定义唯一Symbol标识
const INSTANCE_REF = Symbol('instanceRef')

export abstract class AbstractNode<TParams> implements Omit<INodeType, 'description'> {
  private _executeFunctions: IExecuteFunctions = null

  constructor() {
    // 给实例添加Symbol属性指向自身
    (this as any)[INSTANCE_REF] = this
  }

  set executeFunctions(value: IExecuteFunctions) {
    this._executeFunctions = value
  }

  get executeFunctions(): IExecuteFunctions {
    return this._executeFunctions
  }

  abstract run(t: TParams): Promise<INodeExecutionData>

  async execute(): Promise<INodeExecutionData[][]> {
    this.executeFunctions = this as unknown as IExecuteFunctions
    // 通过Symbol获取类实例
    const instance = (this as any)[INSTANCE_REF]
    await instance.run({ prompts: ['hello', 'world'] } as TParams)

    return this.executeFunctions.prepareOutputData([
      { json: { answer: 'Sample answer' } },
    ])
  }
}

内容的提问来源于stack exchange,提问作者Natan Deitch

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 19:10:29