I am wondering if PowerShell has or will have in the future native support for the Task-based Asynchronous pattern in C#.
It would be nice to have some sugar for async and await but also just being able to work with .NET apps that rely heavily on this pattern (such as Kestrel) without having to worry about managing runspaces would be very nice.
I did find SeeminglyScience has a couple of PowerShell classes for a start on sample implementation as well as some basic functions to support async and await for some ideas on what I'm looking for:
https://www.powershellgallery.com/packages/EditorServicesCommandSuite/0.4.0/Content/Classes%5CAsync.ps1
The referenced code takes a subset of the PowerShell syntax and turns it into .NET delegates but it is essentially a different language (e.g. no commands) . PowerShell concurrency is fundamentally based on runspaces. I wrote an RFC for a task(ish) based model of concurrency a while back Native Support for Concurrent Programming in PowerShell but it hasn't gone anywhere. As far as await goes, I suspect doing the lift-and-split that C# does would be tricky in PowerShell (but I haven't looked at it a whole lot.) Now I do like Golang's model of concurrency with tasks and channels - maybe we should do that :-)
Anyway, can you provide more specific scenarios/examples of what you'd like to do? Even if we can't do a fully general implementation, I sure there's something useful we can do.
Sure thing! So we are in the middle of a rewrite of https://github.com/PowerShell/Polaris to a pure PowerShell implementation and are currently using HttpListener which works fine cross-platform for now but as HttpListener doesn't seem to be getting any development love lately we're looking at the new hotness Kestrel.
We are currently working with HttpListener's event based asynchronous support and using @oising's marvelous function New-ScriptBlockCallback to provide an AsyncCallback. I was hoping to find the same thing in Kestrel but it appears that they only support the task based asynchronous pattern which I can't see an easy way to convert to an event based pattern.
The sample code that I would love to write and just have work is this sample: (simplified from this sample in C#)
Import-Module .\lib\Microsoft.AspNetCore.dll
Import-Module .\lib\Microsoft.AspNetCore.Diagnostics.dll
Import-Module .\lib\Microsoft.AspNetCore.Hosting.dll
Import-Module .\lib\Microsoft.AspNetCore.Server.Kestrel.dll
class StartUp {
[void] Configure([IApplicationBuilder]$App){
[DeveloperExceptionPageExtensions]::UseDeveloperExceptionPage($App)
[RunExtensions]::Run($App, async $context => {
param($context)
return ([Microsoft.AspNetCore.Http.HttpResponseWritingExtensions]::WriteAsync($Context.Response, "Hello World", [Threading.CancellationToken]::None))
})
}
}
#$Env:ASPNETCORE_ENVIRONMENT = "Development"
$WebHostBuilder = $Null
[IWebHostBuilder]$WebHostBuilder = [Microsoft.AspNetCore.WebHost]::CreateDefaultBuilder()
$WebHostBuilder = [WebHostBuilderExtensions]::UseStartup($WebHostBuilder, [StartUp])
$WebHostBuilder = [WebHostBuilderKestrelExtensions]::UseKestrel($WebHostBuilder)
$WebHost = $WebHostBuilder.Build()
$Result = $WebHost.StartAsync()
This is my specific use-case at the moment but I think there are quite a few other .Net projects that rely heavily on the task based asynchronous pattern that are currently fairly complex to work with natively in PowerShell.
Thanks for your thoughts!
@Tiberriver256 Thanks!
So, back when I had more ideas than the focus to complete most of them, I wrote a threads/tasks prototype extension to my old PSEventing library that could handle tasks, apm (iasyncresult) and callbacks. It got annoying as I tried to deal with runspaces, and while I had solutions, it just seemed to unwieldy and inelegant so I just got bored with it. Anyway, you may have some luck if you grok System.Threading.Tasks.Task.FromAsync (APM to Task methods) and the non-public static class System.Threading.Tasks.TaskToApm which goes in the other direction: Task to APM.
Ok, so that works.
find-module poke | install-module -scope currentuser
ipmo poke
$tasksToApm = peek -Name System.Threading.Tasks.TaskToApm
$task = [System.Threading.Tasks.Task]::CompletedTask
$cb = New-ScriptBlockCallback -Callback { write-host "completed!" }
$ar = $tasksToApm.Begin($task, $cb, $null)
completed!
Obviously I'm not suggesting a dependency on my Poke module, but it's just to facilitate the proof of concept here :)
@BrucePay
The referenced code takes a subset of the PowerShell syntax and turns it into .NET delegates but it is essentially a different language (e.g. no commands) .
I believe you're thinking of PSLambda :)
The referenced code was an earlier attempt with a similar idea, but it just creates an lambda expression that invokes the script block against a RunspacePool.
I found this tweet today from David Fowl from AspNet Core Team :
https://twitter.com/davidfowl/status/1004237863913906176
Next, If you ever need to invoke a method on a type via reflection and that method could be async, we have a helper that we use everywhere in the http://ASP.NET Core code base that is highly optimized and flexible. The ObjectMethodExecutor
Do you think this code could be implemented as keyword "await" in PowerShell ?
Would it be possible to implement await as a cmdlet instead of a keyword? The problem I see with JS's await is that it ruins chaining and would have similar impact to PowerShell pipelines. I think the semantics around Jobs circumvent this issue--perhaps we can use similar semantics for Tasks.
1..10 | % {Invoke-WebRequest "https://mysite?i=$_" -Async} | Wait-Task | % {Invoke-Something $_}
Also, when working with tasks and parallel stuff, a lot of functionality is implemented as extension methods, so easy, straight forward access to extension methods would be great in this context.
Couldn't a Task continuation be represented by a specialization of a ThreadJob?
Most helpful comment
Would it be possible to implement
awaitas a cmdlet instead of a keyword? The problem I see with JS'sawaitis that it ruins chaining and would have similar impact to PowerShell pipelines. I think the semantics aroundJobs circumvent this issue--perhaps we can use similar semantics for Tasks.