My goal is to call a class method when the job received or simply to notify outside method that the job is complete.
Is there any way to do this? This is the sample script:
class Test {
$_Trigger
$_Event
$_Job
$_Result
Start() {
$this._Job = Start-Job -Name JobTest -ScriptBlock { Start-Sleep -Seconds 2; return 100; }
$this._Event = Register-ObjectEvent $this._Job StateChanged -Action {
$script:_Result = Receive-Job -Id $sender.Id -Keep
$this.Trigger() # this line here
}
}
Trigger() {
<#
CALL OTHER METHODS
#>
Write-Host "job complete"
}
}
I also try passing the method when calling the job like this
Start-Job -Name JobTest -ScriptBlock { Start-Sleep -Seconds 2; return 100; } -ArgumentList $this._Trigger
and want to access it via $Sender
since there is the $sender
variable as stated by the docs, but my problem is how to call it or how can I "view" this automatic variables inside the script block?
The value of the Action parameter can include the $Event, $EventSubscriber, $Sender, $EventArgs, and $Args automatic variables. These variables provide information about the event to the Action script block. For more information, see about_Automatic_Variables.
CodePudding user response:
Script blocks passed to the -Action
parameter of Register-ObjectEvent
run in a dynamic module, which doesn't have access to the caller's scope.
However, you can pass arbitrary data to the script block via the -MessageData
parameter, which you can access via $Event.MessageData
there, so you can pass the custom-class instance at hand as -MessageData $this
:
class Test {
$_Event
$_Job
$_Result
Start() {
$this._Job = Start-Job -Name JobTest -ScriptBlock { Start-Sleep -Seconds 2; return 100; }
# Note the use of -MessageData and $Event.MessageData
$this._Event =
Register-ObjectEvent $this._Job StateChanged -MessageData $this -Action {
$Event.MessageData._Result = Receive-Job -Id $sender.Id -Keep
$Event.MessageData.Trigger() # this line here
}
}
Trigger() {
<#
CALL OTHER METHODS
#>
Write-Host "job complete"
}
}
CodePudding user response:
A couple of things.
If you want to debug your script or just your script to work when not ran from VsCode (eg: in a production context), you need to make sure that your script do not exit too soon. The simplest way to make sure this does not happen is to have some sort of loop at the end.
If you attempted to place a breakpoint within the Register-ObjectEvent -Action
scriptblock and did have a message stating that the "breakpoint could not be hit", this is because your script already had exited and the debugger detached at the moment when you reached the actual event handler. Since VsCode maintain the session even when your script exited, the event handler will trigger, but that won't be a debuggable version.
$this
will be $null
from within the Register-ObjectEvent -Action
scriptblock context. You need to pass the class as message data -MessageData @{This = $this }
and access it from within the action block as $Event.MessageData.This
You don't have any Try/Catch in the Action
scriptblock. Just be aware that any error will kill the event handler and it won't trigger afterward if any error is thrown.
Here is your example, adapted to fix these issues. (Note: I also added Cyan)
class Test {
$_Trigger
$_Event
$_Job
$_Result
Start() {
$this._Job = Start-Job -Name JobTest -ScriptBlock { Start-Sleep -Seconds 2; return 100; }
$this._Event = Register-ObjectEvent $this._Job StateChanged -Action {
try {
$MyObject = $Event.MessageData.This
$MyObject._Result = Receive-Job -Id $sender.Id -Keep
$MyObject.Trigger() # this line here
}
catch {
}
} -MessageData @{This = $this }
}
Trigger() {
<#
CALL OTHER METHODS
#>
Write-Host "job complete" -ForegroundColor Cyan
}
}
$MyTestInstance = [Test]::new()
$MyTestInstance.Start()
# IMPORTANT
# You need some kind of loop to prevent the script of exiting immediately.
# If you don't have any, It will appear to work in vscode but won't work in production contexts
# You also won't be able to debug the code if the script debugger exitted
# We stay in the loop until we have a result.
while (!$$MyTestInstance._Result) {
Start-Sleep -Milliseconds 100
}
References
VsCode and Powershell: How to debug and trace into code called by Register-ObjectEvent