Search code examples
powershellpowershell-7.0

Adding all module functions into foreach-Object -Parallel block


I have a list of API requests I need to make, and I'm trying to speed it up using Foreach-Object -Parallel:

$funcDef = ${function:Request-Something}.ToString()
$results = [System.Collection.ArrayList]@()

$requests | ForEach-Object -parallel {
    ${function:Request-Something} = $using:funcDef
    $null = ($using:results).add((Request-Something -request $_))
}

However, Request-Something calls a number of other custom functions from the same module, APIHelpers.psm1

I need a way to add all the necessary functions into that script block

I was considering building a hashtable that maps function names to function text, but I can't figure out the syntax for passing a variable to $function:

$funcDefs = @{}
(Get-Command -Module APIHelpers).Name | ForEach-Object {
    $name = $_
    $funcDefs[$name] = ${function:$`{name`}} #This does NOT work, I can't figure out the syntax
}

Once I have this hashtable built, I imagine I could modify the original code:

$requests | ForEach-Object -parallel {
    foreach ($funcName in $using:funcDefs.keys) {
        ${function:$funcName} = $using:funcDefs[$funcName]
    }
    $null = ($using:results).add((Request-Something -request $_))
}

Solution

  • ${function:$`{name`}} #This does NOT work, I can't figure out the syntax

    You're using namespace variable notation, which only supports literal item names.[1]

    Use the equivalent Get-Content call targeting the function: drive, which allows you to use variables:

    $funcDefs[$name] = (Get-Content function:$name).ToString()
    

    Alternatively, you could take a shortcut via Invoke-Expression (iex), which should generally be avoided, however: (Invoke-Expression "`${function:$name}").ToString()


    Taking a step back: Can't you just call Import-Module inside the -Parallel script block?


    Note that, as a future enhancement, copying the caller's state to the parallel threads is being considered, which would mean that all functions the caller sees are implicitly available to the threads too; see GitHub issue #12240.


    [1] In the sense that you cannot use variable names or expressions. The literally specified name is actually - and unexpectedly - interpreted as a wildcard pattern, as discussed in this answer.