从工作中触发事件

时间:2019-06-15 11:55:29

标签: powershell

我对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)

请帮助!

2 个答案:

答案 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++))