【问题标题】:Closures in Groovy not capturing outside variablesGroovy 中的闭包不捕获外部变量
【发布时间】:2019-10-14 08:15:47
【问题描述】:

在 Jenkins 管道的上下文中,我有一些 Groovy 代码枚举一个列表,创建闭包,然后在闭包中使用该值作为在映射中查找另一个值的键。这似乎几乎每次都充斥着某种异常或竞争条件。

这是对代码的简化:

def tasks = [:]
for (platformName in platforms) {
  // ...

  tasks[platformName] = {
    def componentUploadPath = componentUploadPaths[platformName]

    echo "Uploading for platform [${platformName}] to [${componentUploadPath}]."

    // ...
}

tasks.failFast = true
parallel(tasks)

platforms 有两个值。我通常会看到两个迭代和两个注册的任务,tasks 中的键是正确的,但是在闭包中的 echo 语句表明我们只是在其中一个平台上运行了两次:

14:20:02 [platform2] Uploading for platform [platform1] to [some_path/platform1].
14:20:02 [platform1] Uploading for platform [platform1] to [some_path/platform1].

这太荒谬了。

我需要添加什么或做些不同的事情?

【问题讨论】:

  • “几乎每次”?所以你看到它工作了吗?

标签: jenkins groovy jenkins-pipeline jenkins-groovy


【解决方案1】:

这与您在 Javascript 中看到的问题相同。

当你在 for 循环中生成闭包时,它们被绑定到一个变量,而不是变量的

当循环退出并运行闭包时,它们都将使用相同的值......即 - for 循环中退出之前的最后一个值

例如,您希望以下内容打印 1 2 3 4,但事实并非如此

def closures = []

for (i in 1..4) {
    closures << { -> println i }
}

closures.each { it() }

它打印4 4 4 4

要解决此问题,您需要做以下两件事之一...首先,您可以在本地范围的变量中捕获 ,然后关闭此变量:

for (i in 1..4) {
    def n = i
    closures << { -> println n }
}

您可以做的第二件事是使用 groovy 的 eachcollect,因为每次调用它们时,变量都是不同的实例,因此它再次起作用:

(1..4).each { i ->
    closures << { -> println i }
}

对于您的情况,您可以循环使用 platforms 同时使用 collectEntries 收集到地图中:

def tasks = platforms.collectEntries { platformName ->
  [
     platformName,
     { ->
        def componentUploadPath = componentUploadPaths[platformName]
        echo "Uploading for platform [${platformName}] to [${componentUploadPath}]."
     }
  ]
}

希望这会有所帮助!

【讨论】:

  • 只使用$it 怎么样?在我看来,在这种情况下,Groovy 会在每个闭包中自动将映射中的键分配为 $it,因此只需设置 def platformName = $it
  • 使用 for 循环?没有it
  • 不用担心 ?? 很高兴我能帮上忙
  • 抱歉,刚刚注意到我的each 示例过于复杂。现已修复?
  • closures*.call() 然后打高尔夫球 ;)
猜你喜欢
  • 2014-03-15
  • 1970-01-01
  • 2016-02-13
  • 2015-08-14
  • 2014-06-05
  • 2016-01-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多