This is working code:
$ids = 1..9
$status = [PSCustomObject[]]::new(10)
foreach ($id in $ids)
{
$uriStr = "http://192.168." + [String]$id + ".51/status"
$uri = [System.Uri] $uriStr
$status[$id] = try {Invoke-RestMethod -Uri $uri -TimeOut 30}catch{}
}
$status
I would like to execute the ForEach loop in parallel to explore performance improvements.
First thing I tried (turned out naive) is to simply introduce the -parallel parameter
$ids = 1..9
$status = [PSCustomObject[]]::new(10)
foreach -parallel ($id in $ids)
{
$uriStr = "http://192.168." + [String]$id + ".51/status"
$uri = [System.Uri] $uriStr
$status[$id] = try {Invoke-RestMethod -Uri $uri -TimeOut 30}catch{}
}
$status
This results in the following error, suggesting this feature is still under consideration of development as of Powershell 7.3.9:
ParserError:
Line |
3 | foreach -parallel ($id in $ids)
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| The foreach '-parallel' parameter is reserved for future use.
I say naive because the documentation says the parallel parameter is only valid in a workflow. However, when I try it I get an error saying workflow is no longer supported.
workflow helloworld {Write-Host "Hello World"}
ParserError:
Line |
1 | workflow helloworld {Write-Host "Hello World"}
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Workflow is not supported in PowerShell 6+.
Then I tried various combinations from various references (Good Example), which advise about ForEach being fundamentally different from from ForEach-Object, which supports parallel, like so (basically piping the ids in):
$ids = 1..9
$status = [PSCustomObject[]]::new(10)
$ids | ForEach-Object -Parallel
{
$uriStr = "http://192.168." + [String]$_ + ".51/status"
$uri = [System.Uri] $uriStr
$status[$_] = try {Invoke-RestMethod -Uri $uri -TimeOut 30}catch{}
}
$status
This generates the following error:
ForEach-Object:
Line |
3 | $ids | foreach-object -parallel
| ~~~~~~~~~
| Missing an argument for parameter 'Parallel'. Specify a parameter of type
| 'System.Management.Automation.ScriptBlock' and try again.
$uriStr = "http://192.168." + [String]$_ + ".51/status"
$uri = [System.Uri] $uriStr
$status[$i_] = try {Invoke-RestMethod -Uri $uri -TimeOut 30}catch{}
But, after trying various script block semantics, here is the best I could do (basically apply :using to status variable that is outside the script block):
$ids = 1..9
$status = [PSCustomObject[]]::new(10)
$myScriptBlock =
{
$uriStr = "http://192.168." + [String]$_ + ".51/status"
$uri = [System.Uri] $uriStr
{$using:status}[$_] = try {Invoke-RestMethod -Uri $uri -TimeOut 30}catch{}
}
$ids | foreach-object -parallel $myScriptBlock
$status
Error, again: Unable to index into Scriptblock
Line |
4 | … ng:status}[$_] = try {Invoke-RestMethod -Uri $uri -TimeOut 30}catch{}
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Unable to index into an object of type "System.Management.Automation.ScriptBlock".
InvalidOperation:
There are couple of other worthy to mention errors - if not applying the :using qualifier, get error
"cannot index into null array"
this basically means the $status variable is unrecognizable in the foreach or script block.
All other ways to express the :using qualifier are rejected with errors like
"assignment expression invalid" "use {}..."
so have been omitted for brevity and, better flow in problem statement. Lastly, here is a reference on SciptBlocks, for Powershell 7.3+ which have also been considered without much progress.
-Parallel. 😉$ids | ForEach-Object -Parallel {