I did a bit of research on how to call an async method in a sync method in Epi, but I could not find the "final" recommendation. So let's say inside of the method ProcessPayment present in the IPaymentPlugin I want to call an async method, what is the best way? I found the async helper below but I'm not sure if Episer recommends it. Let me know your thoughts, thank you!
public static class AsyncHelper
private static readonly TaskFactory _taskFactory = new
public static TResult RunSync<TResult>(Func<Task<TResult>> func)
public static void RunSync(Func<Task> func)
I use the same helper methods all the time, when I need to call async code from a sync method (for instance payment plugins). I have never had any issues with it.
So I would definitely say, go ahead.
This is not Episerver-related question - just saying. I don't think you need to use TaskFactory at all. Just Task.Run is enough
I'll get the popcorns now :D
@Valdis, where are you? you need to join to this topic.
I suppose it would be good to reference the sources of "I copied this helper from somewhere", most likely we can link to this SO-thread because the code in the initial post looks like copy+paste of MS internal asynchelper.
I know I have used in some code: xxxx.GetAwaiter().GetResult();
Also have spent some time on looking for the real solution of the safe way "calling from sync async code".
@Quan yes not an Episerver issue as is BUT Episerver has in some places only async implementation which you must call from sync and that becomes kind of Episerver issue because there are no "sync" overloads, it would be nice to have "Episerver" recommendation how to call - at least when it is wrong, it is easy to fix :D
I've also looked at https://docs.microsoft.com/en-us/archive/blogs/jpsanders/asp-net-do-not-use-task-result-in-main-context for reference and I do know I have somewhere countless amount of links to different postings about this - but the problem is always that one needs to understand the context.
Read also Stephen Cleary old but valid MSDN Magazine article: https://docs.microsoft.com/en-us/archive/msdn-magazine/2015/july/async-programming-brownfield-async-development
(and his blog has tons of info https://blog.stephencleary.com/2012/02/async-and-await.html there is a starting point)
And also his answer in SO: https://stackoverflow.com/questions/9343594/how-to-call-asynchronous-method-from-synchronous-method-in-c/9343733#9343733
@Valdis, your turn to paste links here ;-)
Using the AsyncHelper describe on your code, will not help you on long running tasks if need, for example. Task.Run is quite simitar with TaskFactory.StartNew, that automatic unwrap the results and have default values, but does not allow you to control how to behave and choose the scheduler, like if you really want that thread to be used is from the thread pool.
As Antti, presented, lot's of blogs and posts about this. I think for each case, it must be choosed quite carefull which to use.
I've also used this helper and clearly I'm not the only one! Here's a whole blog post by Māris which reaches the same conclusion (albeit in the context of events): https://marisks.net/2017/04/02/calling-async-methods-within-episerver-events/
@Antti: I actually remember Valdis recommending this same helper on a forum question a while back (but related to Scheduled Jobs): https://world.episerver.com/forum/developer-forum/-Episerver-75-CMS/Thread-Container/2019/5/safe-way-to-call-async-method-in-episerver-scheduled-job/
Of course, there are a lof of caveats here and it really depends on your implementation and the async method you're calling.
And here is a good explanation of the Task.Run vs Task.Factory.StartNew: https://devblogs.microsoft.com/pfxteam/task-run-vs-task-factory-startnew/
So when reading that, I would say why use the helper method from the first post when you could just use: Task.Run(...)
And if you read post from Stephen about the same subject: https://blog.stephencleary.com/2013/08/startnew-is-dangerous.html
The last line says: Just use Task.Run(() => A());
So we have the same mess now in this forum thread about calling async from sync what we have is SO and all over the place :D
Mess is everywhere around async, even after many years of introduction of the subject to the .NET world. Why? Because there is not silver bullet and it depends.
Async is not easy and one should really understand the context and consequences of each of the technique and mechanics behind.
@Jake – AsyncHelper is the easiest way to call async method in context when your async code is “slim” enough and does not require much (request contexts, etc.) Therefore I replied “give it a try” :)
My 2 humble cents here would be - @Quan, yes it is Episerver related topic. Why? Because Episerver is constraining developer and setting is in “sync box”. Of course this is easy to say, but if you look at many modern / latest versions of the libraries, helpers, frameworks, etc. – many of them are providing only “async” path. For example the same ASP.NET Core. So IMHO if I have to use library that provides just async methods in a “runtime” (Episerver) that executes only in sync context (in specific components - like scheduled jobs and other extension points) – it is a problem of a “underlying” framework and not the developer who might be forced to call async methods.
And to follow up: https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-5/