我对Powershell非常陌生。 如果您对Powershell有所了解,我的问题很简单。
在下面的代码中,我试图从作为作业异步运行的一段代码中触发一个事件。由于某些原因,该代码无法正常工作。
$callback = {
param($event);
Write "IN CALLBACK";
};
$jobScript = {
while ($true) {
sleep -s 1;
"IN JOB SCRIPT";
New-Event myEvent;
}
}
Register-EngineEvent -SourceIdentifier myEvent -Action $callback;
Start-Job -Name Job -ScriptBlock $jobScript;
while ($true) {
sleep -s 1;
"IN LOOP";
}
预期输出:
IN LOOP
IN JOB SCRIPT
IN CALLBACK
IN LOOP
IN JOB SCRIPT
IN CALLBACK
...
实际输出:
IN LOOP
IN LOOP
IN LOOP
IN LOOP
...
阅读后,我改变了这一行
Start-Job -Name Job -ScriptBlock $jobScript
到
Start-Job -Name Job -ScriptBlock $jobScript | Wait-Job | Receive-Job;
我根本没有任何输出,因为工作永远不会结束。
有点不同步,但并非如此。 在JS中完成将非常简单。
const fireEvent = (eventName) => { ... }
const subscribeToEvent = (eventName, callback) => { ... }
const callback = () => console.log('IN CALLBACK')
subscribeToEvent('myEvent', callback);
setInterval(() => {
console.log('IN LOOP')
fireEvent('myEvent');
}, 1000)
请帮助!
答案 0 :(得分:4)
运行:
while ($true) {
sleep -s 1;
"IN LOOP";
}
将始终只给您:
IN LOOP
IN LOOP
IN LOOP
IN LOOP
运行此块:
$callback = {
param($event);
Write "IN CALLBACK";
};
$jobScript = {
while ($true) {
sleep -s 1;
"IN JOB SCRIPT";
New-Event myEvent;
}
}
Register-EngineEvent -SourceIdentifier myEvent -Action $callback;
Start-Job -Name Job -ScriptBlock $jobScript;
给您一份名为Job的工作。该作业将一直运行,直到您停止它为止。作业的输出将如下所示:
get-job Job | receive-job
IN JOB SCRIPT
RunspaceId : cf385728-926c-4dda-983e-6a5cfd4fd67f
ComputerName :
EventIdentifier : 1
Sender :
SourceEventArgs :
SourceArgs : {}
SourceIdentifier : myEvent
TimeGenerated : 6/15/2019 3:35:09 PM
MessageData :
IN JOB SCRIPT
RunspaceId : cf385728-926c-4dda-983e-6a5cfd4fd67f
ComputerName :
EventIdentifier : 2
Sender :
SourceEventArgs :
SourceArgs : {}
SourceIdentifier : myEvent
TimeGenerated : 6/15/2019 3:35:10 PM
MessageData :
...
JavaScript对我来说真的是希腊语,但是看来您的问题是该事件未在工作范围内注册。如果您将注册移到工作上,它的行为会像您期望的那样。
如果您这样做:
$jobScript = {
$callback = {
Write-Host "IN CALLBACK"
}
$null = Register-EngineEvent -SourceIdentifier myEvent -Action $callback
while ($true) {
sleep -s 1
Write-Host "IN JOB SCRIPT"
$Null = New-Event myEvent
}
}
$Null = Start-Job -Name Job -ScriptBlock $jobScript;
while ($true) {
sleep -s 1
"IN LOOP"
Get-Job -Name Job | Receive-Job
}
运行$null = Register-EngineEvent ...
或$null = Start-Job ...
时,应避免这些命令创建的对象显示在控制台上。此外,您无需在PowerShell中以;
终止行。
答案 1 :(得分:1)
补充Axel Anderson's helpful answer:
Register-EngineEvent
订阅当前会话中的事件,而用Start-Job
启动的命令在作为子进程的后台作业中运行< / em>,根据定义,这是一个单独的会话。
类似地,您用New-Event
引发的任何事件仅在同一会话中可见,这就是为什么呼叫会话从未看到这些事件的原因。
通过将所有事件逻辑转移到后台作业中,如Axel的回答一样,事件 在后台作业中被处理 ,但是有一个重要的限制-可能是可能对您来说并不重要:
您将无法从事件处理程序中捕获输出:尽管您可以使用Write-Host
将其输出 print打印到控制台,但该输出无法捕获以用于程序化处理。此外,不能{em>抑制这样的Write-Host
输出。
相比之下,直接从后台作业发送到成功输出流的输出-例如"IN JOB SCRIPT"
是隐式的(暗示使用{{1 }})-可以通过Write-Output
捕获 ,它会从后台作业中检索输出。
因此,在您的情况下,以下可能就足够了,根本不需要使用事件:
Receive-Job
有关# Code to execute in the background, defined a script block.
$jobScript = {
while ($true) {
Start-Sleep -Seconds 1
"JOB OUTPUT #$(($i++))"
}
}
# Launch the background job and save the job object in variable $job.
$job = Start-Job -Name Job -ScriptBlock $jobScript;
# Periodically relay the background job's output.
while ($true) {
Start-Sleep -Seconds 1
"IN LOOP, about to get latest job output:"
$latestOutput = Receive-Job $job
"latest output: $latestOutput"
}
构造的说明,请参见this answer。
上面的结果如下:
$(($i++))