Public/Psrunner/Wait-Task.ps1

function Wait-Task {
  # .SYNOPSIS
  # Waits for a scriptblock or job to complete
  # .OUTPUTS
  # TaskResult.Result
  [CmdletBinding(DefaultParameterSetName = 'ScriptBlock')]
  [OutputType([psobject])][Alias('await')]
  param (
    [Parameter(Mandatory = $true, Position = 0, ParameterSetName = '__AllparameterSets')]
    [Alias('m')][ValidateNotNullOrEmpty()]
    [string]$progressMsg,

    [Parameter(Mandatory = $true, Position = 1, ParameterSetName = 'scriptBlock')]
    [Alias('s')][ValidateNotNullOrEmpty()]
    [scriptblock]$scriptBlock,

    [Parameter(Mandatory = $true, Position = 1, ParameterSetName = 'job')]
    [Alias('j')][ValidateNotNullOrEmpty()]
    [System.Management.Automation.Job]$xJob
  )
  begin {
    $Result = TaskResult($null);
    # $Tasks = @()
  }
  process {
    if ($PSCmdlet.ParameterSetName -in ('job', 'scriptBlock')) {
      [System.Management.Automation.Job]$_Job = $(if ($PSCmdlet.ParameterSetName -eq 'scriptBlock') { Get-Job -Id $(Start-Job -ScriptBlock $scriptBlock).Id } else { $Job })
      [Console]::CursorVisible = $false;
      [ProgressUtil]::frames = [ProgressUtil]::_twirl[0]
      [int]$length = [ProgressUtil]::frames.Length;
      $originalY = [Console]::CursorTop
      while ($_Job.JobStateInfo.State -notin ('Completed', 'failed')) {
        for ($i = 0; $i -lt $length; $i++) {
          [ProgressUtil]::frames | ForEach-Object { [Console]::Write("$progressMsg $($_[$i])") }
          [System.Threading.Thread]::Sleep(50)
          [Console]::Write(("`b" * ($length + $progressMsg.Length)))
          [Console]::CursorTop = $originalY
        }
      }
      # i.e: Gives an illusion of loading animation.
      [void][cli]::Write("`b$progressMsg", [ConsoleColor]::Blue)
      [System.Management.Automation.Runspaces.RemotingErrorRecord[]]$Errors = $_Job.ChildJobs.Where({
          $null -ne $_.Error
        }
      ).Error;
      $LogMsg = ''; $_Success = ($null -eq $Errors); $attMSg = Get-AttemptMSg;
      if (![string]::IsNullOrWhiteSpace($attMSg)) { $LogMsg += $attMSg } else { $LogMsg += "... " }
      if ($_Job.JobStateInfo.State -eq "Failed" -or $Errors.Count -gt 0) {
        $errormessages = ""; $errStackTrace = ""
        if ($null -ne $Errors) {
          $errormessages = $Errors.Exception.Message -join "`n"
          $errStackTrace = $Errors.ScriptStackTrace
          if ($null -ne $Errors.Exception.InnerException) {
            $errStackTrace += "`n`t"
            $errStackTrace += $Errors.Exception.InnerException.StackTrace
          }
        }
        $Result = TaskResult($_Job, $Errors);
        $_Success = $false; $LogMsg += " Completed with errors.`n`t$errormessages`n`t$errStackTrace"
      } else {
        $Result = TaskResult($_Job)
      }
      Write-Log $LogMsg -s:$_Success
      [Console]::CursorVisible = $true; Set-AttemptMSg ' '
    } else {
      # $Tasks += $Task
      # While (![System.Threading.Tasks.Task]::WaitAll($Tasks, 200)) {}
      # $Tasks.ForEach( { $_.GetAwaiter().GetResult() })
      $threads = New-Object System.Collections.ArrayList;
      $result = [PSCustomObject]@{
        Task   = $null
        Shell  = $PowerShell
        Result = $PowerShell.BeginInvoke()
      }
      $threads.Add($result) | Out-Null;
      $completed = $false; $_r = @{ true = 'Completed'; false = 'Still open' }
      while ($completed -eq $false) {
        $completed = $true;
        foreach ($thread in $threads) {
          $result.Task = $thread.Shell.EndInvoke($thread.Result);
          $threadHandle = $thread.Result.AsyncWaitHandle.Handle;
          $threadIsCompleted = $thread.Result.IsCompleted;
          Write-Verbose ("[Task] ThreadHandle {0} is {1}" -f $threadHandle, $_r["$threadIsCompleted"])
          if ($threadIsCompleted -eq $false) {
            $completed = $false;
          }
        }
        Start-Sleep -Milliseconds 500;
      }
      foreach ($thread in $threads) {
        $thread.Shell.Dispose();
      }
      return $result
      throw [System.NotSupportedException]::new("Sorry, ParameterSetName task is not yet supported")
    }
  }
  end {
    return $Result
  }
}