Groovy 中的闭包未捕获外部变量
Closures in Groovy not capturing outside variables
在 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].
太可笑了
我需要添加或做哪些不同的事情?
这与您在 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 的 each
或 collect
因为每次调用它们时,变量都是不同的实例,因此它再次起作用:
(1..4).each { i ->
closures << { -> println i }
}
对于您的情况,您可以使用 collectEntries
:
循环遍历 platforms
和 同时收集到地图中
def tasks = platforms.collectEntries { platformName ->
[
platformName,
{ ->
def componentUploadPath = componentUploadPaths[platformName]
echo "Uploading for platform [${platformName}] to [${componentUploadPath}]."
}
]
}
希望对您有所帮助!
在 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].
太可笑了
我需要添加或做哪些不同的事情?
这与您在 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 的 each
或 collect
因为每次调用它们时,变量都是不同的实例,因此它再次起作用:
(1..4).each { i ->
closures << { -> println i }
}
对于您的情况,您可以使用 collectEntries
:
platforms
和 同时收集到地图中
def tasks = platforms.collectEntries { platformName ->
[
platformName,
{ ->
def componentUploadPath = componentUploadPaths[platformName]
echo "Uploading for platform [${platformName}] to [${componentUploadPath}]."
}
]
}
希望对您有所帮助!