Skip to content

PowerShell Background Jobs

How to run PowerShell commands asynchronously with background jobs using Start-Job, monitor progress with Get-Job, retrieve results via Receive-Job, and manage parallel execution efficiently.

A background job is an asynchronous execution unit that runs a command or script in the background without blocking the current interactive session. PowerShell's *-Job cmdlets let you start, monitor, retrieve results from, and clean up background jobs. Background jobs enable concurrent script execution, allowing long-running tasks such as network scans, file transfers, or bulk administrative queries to process in parallel while the primary console remains responsive.

Starting a Job with Start-Job

The Start-Job cmdlet launches a script block or external script as an independent, non-interactive background job on the local system. It immediately returns control to the console.

Consolepowershell
Start-Job -ScriptBlock { Get-Process }

where -ScriptBlock is the code to execute in the background job.

Outputplaintext
Id     Name            PSJobTypeName   State         HasMoreData     Location             Command
--     ----            -------------   -----         -----------     --------             -------
7      Job7            BackgroundJob   Running       True            localhost             Get-Process

Starting a Job with a Name

Assign a friendly name using -Name to make jobs easier to identify later.

Consolepowershell
Start-Job -Name "ProcessSnapshot" -ScriptBlock { Get-Process }

where -Name is a custom identifier for the job, used instead of the auto-generated Job1, Job2, etc.

Outputplaintext
Id     Name            PSJobTypeName   State         HasMoreData     Location             Command
--     ----            -------------   -----         -----------     --------             -------
9      ProcessSnapshot BackgroundJob   Running       True            localhost             Get-Process

Starting a Job That Runs a Script File

Use -FilePath to run an entire .ps1 script as a background job.

Consolepowershell
Start-Job -Name "BackupJob" -FilePath "C:\Scripts\backup.ps1"

where -FilePath is the path to the .ps1 script to execute as a background job.

Detailed Example

start-job-demo.ps1powershell
$job = Start-Job -Name "DataProcessor" -ScriptBlock {
    Start-Sleep -Seconds 3
    Get-Service -Name "wuauserv" | Select-Object -Property Name, Status, StartType
}

$job | Select-Object -Property Id, Name, State, HasMoreData, Location

where:

  • Start-Job is the core cmdlet that launches commands into a background session.
  • -Name assigns a user-friendly identifier to the job instance.
  • -ScriptBlock contains the code statements to be executed asynchronously.
  • Id is the system-assigned sequential integer identifier for the job.
  • State is the current operational state (e.g., NotStarted, Running, Completed, Failed).
  • HasMoreData is a boolean indicating whether the job holds unread output streams.
  • Location specifies the host where the job is executing (localhost for local jobs).
Consolepowershell
./start-job-demo.ps1
Outputplaintext
Id          : 11
Name        : DataProcessor
State       : Running
HasMoreData : True
Location    : localhost

Monitoring Jobs with Get-Job

The Get-Job cmdlet lists all jobs in the current session along with their state.

Consolepowershell
Get-Job
Outputplaintext
Id  Name          State         HasMoreData     Location    Command
--  ----          -----         -----------     --------    -------
1   ProcessSnapshot Completed   True            localhost   Get-Process
2   BackupJob      Running      True            localhost   C:\Scripts\backup.ps1

where:

  • State is the job's current status: NotStarted, Running, Completed, Failed, or Stopped.
  • HasMoreData indicates whether the job has output waiting to be collected.

Waiting for a Job with Wait-Job

The Wait-Job cmdlet pauses script execution until a specified job (or jobs) finishes running.

Consolepowershell
Wait-Job -Name "BackupJob"

where -Name is the name of the job to wait for.

Consolepowershell
Wait-Job -Name "BackupJob" -Timeout 60

where -Timeout is the maximum number of seconds to wait before returning control, even if the job hasn't finished.

Waiting for Multiple Jobs

The Wait-Job cmdlet can monitor several jobs at once. By default, it waits for all specified jobs to conclude. Supplying the -Any switch instructs Wait-Job to resume execution as soon as the first job in the list finishes.

wait-job-demo.ps1powershell
$job1 = Start-Job -Name "WorkerA" -ScriptBlock { Start-Sleep -Seconds 2; "Worker A Finished" }
$job2 = Start-Job -Name "WorkerB" -ScriptBlock { Start-Sleep -Seconds 4; "Worker B Finished" }

Write-Output "Waiting for background workers to finish..."

$completed = Wait-Job -Job $job1, $job2 -Timeout 10

$results = Receive-Job -Job $completed

Write-Output $results

where:

  • Wait-Job blocks the parent shell until the monitored jobs finish execution.
  • -Job specifies one or more job objects or IDs to monitor.
  • -Timeout is the maximum duration (in seconds) the shell will wait before returning control, preventing endless blocking if a background worker hangs.
  • Receive-Job emits the results collected across all completed job objects passed to it.
Consolepowershell
./wait-job-demo.ps1
Outputplaintext
Waiting for background workers to finish...
Worker A Finished
Worker B Finished

Retrieving Job Results with Receive-Job

The Receive-Job cmdlet collects the output produced by a background job.

run-and-receive-job.ps1powershell
$job = Start-Job -ScriptBlock { Get-Process | Where-Object { $_.WS -gt 100MB } }
Wait-Job -Job $job
Receive-Job -Job $job

where:

  • $job stores the job object returned by Start-Job.
  • Wait-Job -Job $job blocks until the job finishes.
  • Receive-Job -Job $job retrieves and displays the job's output.
Consolepowershell
./run-and-receive-job.ps1
Outputplaintext
Id     Name            PSJobTypeName   State         HasMoreData     Location             Command
--     ----            -------------   -----         -----------     --------             -------
21     Job21           BackgroundJob   Completed     True            localhost             Get-Process | Where-O...

Id      : 3636
Handles : 2015
CPU     : 7.625
Name    : explorer


Id      : 1760
Handles : 0
CPU     :
Name    : Memory Compression


Id      : 10092
Handles : 892
CPU     :
Name    : MsMpEng


Id      : 3572
Handles : 913
CPU     : 5.25
Name    : powershell

Job Lifecycle Example

job-lifecycle.ps1powershell
Get-Job -Name "DataProcessor"

$result = Receive-Job -Name "DataProcessor" -Keep

$result

Remove-Job -Name "DataProcessor"

where:

  • Get-Job lists all background jobs initiated in the current session and displays their current states.
  • Receive-Job fetches the standard output, warning, error, and verbose streams generated inside the background script block.
  • -Keep preserves the output cache in memory. By default, Receive-Job clears the job's internal buffer upon retrieval; adding -Keep allows data to be read multiple times.
  • Remove-Job deletes the job entry and its associated runtime memory from the current shell session.
Consolepowershell
./job-lifecycle.ps1
Outputplaintext
Id     Name            PSJobTypeName   State         HasMoreData     Location             Command
--     ----            -------------   -----         -----------     --------             -------
11     DataProcessor   BackgroundJob   Completed     True            localhost            ...

Name       : wuauserv
Status     : Running
StartType  : Manual
RunspaceId : 09822064-e095-4343-b975-703ec467b9b6

Removing Jobs with Remove-Job

The Remove-Job cmdlet deletes a job object from the session once it's no longer needed.

Consolepowershell
Remove-Job -Name "BackupJob"

where -Name is the name of the job to remove.

To clean up all completed jobs at once:

Consolepowershell
Get-Job | Where-Object { $_.State -eq "Completed" } | Remove-Job

Practical Usage: Running Multiple Tasks in Parallel

A common use case for jobs is running several independent, time-consuming tasks concurrently instead of sequentially.

parallel-server-check.ps1powershell
$servers = "SRV01", "SRV02", "SRV03"

foreach ($server in $servers) {
    Start-Job -Name $server -ScriptBlock {
        param($computer)
        Test-Connection -ComputerName $computer -Count 2 -Quiet
    } -ArgumentList $server
}

Get-Job | Wait-Job

foreach ($server in $servers) {
    $result = Receive-Job -Name $server
    Write-Output "$server reachable: $result"
}

Get-Job | Remove-Job

where:

  • -ArgumentList passes external values into the job's script block as parameters.
  • Get-Job | Wait-Job waits for all started jobs to complete before proceeding.
  • Receive-Job -Name $server retrieves the result for each individual server's job.
Consolepowershell
./parallel-server-check.ps1
Outputplaintext
SRV01 reachable: True
SRV02 reachable: True
SRV03 reachable: False

Practical Administration: Parallel Connectivity Sweep

Administrators often need to probe multiple remote endpoints simultaneously. Running connection tests sequentially causes severe delays, whereas jobs allow checking all targets in parallel.

parallel-network-audit.ps1powershell
$servers = @("127.0.0.1", "10.0.0.1", "localhost")

$jobs = foreach ($server in $servers) {
    Start-Job -Name "Ping_$server" -ArgumentList $server -ScriptBlock {
        param($target)
        $ping = Test-Connection -ComputertName $target -Count 1 -Quiet
        [PSCustomObject]@{
            Node    = $target
            Online  = $ping
            CheckAt = (Get-Date -Format "HH:mm:ss")
        }
    }
}

$null = Wait-Job -Job $jobs -Timeout 5

$report = Receive-Job -Job $jobs
Remove-Job -Job $jobs -Force

$report | Format-Table -AutoSize

where:

  • param($target) accepts inputs passed into the background script block from the parent scope.
  • -ArgumentList maps outer loop variables into the isolated job's parameter block.
  • Test-Connection -Quiet returns a fast $true/$false boolean indicating ICMP echo reply.
  • $null = Wait-Job suppresses the raw job status table emitted by Wait-Job, keeping the console output clean.
  • Remove-Job -Force removes the jobs even if an individual job failed or remained active past the timeout threshold.
Consolepowershell
./parallel-network-audit.ps1
Outputplaintext
Node      Online CheckAt 
----      ------ ------- 
127.0.0.1   True 10:24:15
10.0.0.1   False 10:24:18
localhost   True 10:24:15

Jobs vs Runspaces

Start-Job runs each job in a separate process, which provides strong isolation but comes with higher memory and startup overhead. For lighter-weight parallelism, PowerShell also supports runspaces and Start-ThreadJob (from the ThreadJob module), which run concurrently within the same process using threads instead of separate processes.

PowerShell supports multiple paradigms for concurrent and parallel execution. Understanding the architectural differences between traditional background jobs and runspaces determines script scalability:

Feature Standard Jobs (Start-Job) Thread Jobs / Runspaces (Start-ThreadJob / ForEach-Object -Parallel)
Execution Model Separate OS Process (powershell.exe / pwsh.exe) Separate Threads within the same host process
Memory Footprint High (~30-60 MB per job instance) Minimal (~1-2 MB per thread)
Startup Latency Slow (requires full shell environment initialization) Near-instantaneous thread execution
Process Isolation Full isolation; crashed child process won't kill parent Shared process; unhandled memory faults can crash host
Variable Access Must pass variables explicitly via ArgumentList or $using: Can directly read shared objects and state
Navigation