在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
中的键是正确的,但是在闭包内的回声语句 表示我们只是在其中一个平台上运行了两次:
14:20:02 [platform2] Uploading for platform [platform1] to [some_path/platform1].
14:20:02 [platform1] Uploading for platform [platform1] to [some_path/platform1].
太荒谬了。
我需要添加什么或做不同的事情?
答案 0 :(得分:3)
这与您在Javascript中看到的问题相同。
在for循环中生成闭包时,它们绑定到变量,而不是变量的 value 。
当循环退出并运行闭包时,它们都将使用相同的值...即-for循环中退出前的最后一个值
例如,您希望以下内容打印1 2 3 4
,但不会打印
def closures = []
for (i in 1..4) {
closures << { -> println i }
}
closures.each { it() }
它打印4 4 4 4
要解决此问题,您需要执行以下两项操作之一:首先,可以在局部范围内的变量中捕获 value ,然后关闭该变量:
for (i in 1..4) {
def n = i
closures << { -> println n }
}
您可以做的第二件事是每次调用它们时都使用groovy的each
或collect
,该变量是一个不同的实例,因此它可以再次工作:
(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}]."
}
]
}
希望这会有所帮助!