jenkins - Groovy 中的闭包不捕获外部变量
问题描述
在 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 }
}
对于您的情况,您可以使用以下命令同时循环platforms
并收集到地图中collectEntries
:
def tasks = platforms.collectEntries { platformName ->
[
platformName,
{ ->
def componentUploadPath = componentUploadPaths[platformName]
echo "Uploading for platform [${platformName}] to [${componentUploadPath}]."
}
]
}
希望这可以帮助!
推荐阅读
- c# - 我如何序列化 Json 字符串中的 c# 关键字
- angular - Ionic3 Angular 不允许从视频播放器(Android 和 iOS)访问本地视频文件
- swift - 主-明细界面项目的明细界面中,撤消不起作用
- python - 使用条件语句在矩阵中输入值导致错误
- flutter - 如何在卡片顶部叠加圆形图像(一半在卡片内,一半在卡片外)?
- c# - 满足条件时防止从 OnClick 执行按钮命令
- mysql - MySQL 类型列表以及每种类型的总和
- python - 通过 POST 请求发送序列化数据
- sql - 欺骗 SQL 将数据读取为 true 而不是 false
- c# - 通用 ViewModel 类