How to make task from asyc - c#

I have a call I need to wait to finish to proceed. This is a begincreate for an opportunitySet and I'm having issues converting this into a task to wait for.
_context.BeginSaveChanges(OnCreateOpportunityComplete, _context.OpportunitySet);
/// <summary>
/// Callback method invoked when Account is done being created.
/// </summary>
/// <param name="result"></param>
private void OnCreateOpportunityComplete(IAsyncResult result)
{
try
{
_context.EndSaveChanges(result);
Opportunity createdOpportunity = result.AsyncState as Opportunity;
BeginRetrieveOpportunity(createdOpportunity.OpportunityId);
}
catch (SystemException se)
{
_syncContext.Send(new SendOrPostCallback(showErrorDetails), se);
}
}
private void BeginRetrieveOpportunity(Guid _id)
{
try
{
DataServiceQuery<Opportunity> query = (DataServiceQuery<Opportunity>)_context
.OpportunitySet.Where<Opportunity>(o => o.OpportunityId == _id);
query.BeginExecute(OnRetrieveOpportunityComplete, query);
}
catch (DataServiceQueryException dsqe)
{
_syncContext.Send(new SendOrPostCallback(showErrorDetails), dsqe);
}
}
Does anyone know how to handle this? I've tried numerous times and there seems to be some issues when creating the task. Or if there is any other way for this to wait for the multiple opportunities in the set to be created that would be awesome too.
EDIT: To clarify, the first line in the code is a line in my main function where after I gather all the opportunities I want to add I call the create methods to call the inserts provided in the CRM kit. This does insert, but by the time the program reaches its end more often than not all of the opportunities have not been created.

When making your method, make sure to mark it as "async". Then when calling it don't use the keyword "await" and you will get the its Task. From their you can call yourTask.Wait(). Which will wait until the execution of that method has finalized.

I couldn't really tell what method that you wanted to make async because your question is a little unclear, but if you follow this simple method, you should be able to make your own async method. Let's say you start with this method:
private int DoSomething(int someValue)
{
...
}
We can run it with async in this way:
private async int DoSomethingAsync(int someValue)
{
int result = await Task.Run(() => DoSomething(someValue));
// Do something with result here when it arrives
}
UPDATE >>>
You can use the Task.Wait method in Silverlight. If you take a look at the How to: Wait on One or More Tasks to Complete page on MSDN, you can get an idea of how it is done. This page is only for .NET 4 and 4.5 so I don't know if it will all work with Silverlight but you can try. You can still use your method in a Task like I showed you in the example above.

Related

Task chaining in Firebase Unity, how to avoid code pyramid?

Supposed I have to get login information from the server then login, then after the login do something next. The goal is to encapsulate all of this into something that caller can wait.
However each of Firebase C# API call already returned separate tasks (that has already been started). My approach is to think of how to chain each of these separate task together.
I am thinking of making a new Task that represent this since it also seems great for aggregating all the exceptions for the caller to catch.
We have
Task<DataSnapshot> getKeyTask = Database.Child("keyLocation").GetValueAsync();
GetValueAsync as I understand already spun up the task. We can observe the returned task or better use continuation to begin something when it finished.
Task loginTask = Auth.SignInWithEmailAndPasswordAsync(username, password);
Auth.SignInWithEmailAndPasswordAsync also spun up a new task in itself. I can also use continuation to wait and begin something else.
After the login I would like to use another database call to get user data.
So
public Task Login()
{
Task<DataSnapshot> getKeyTask = Database.Child("keyLocation").GetValueAsync();
Task loginAfterGetKeyTask = getKeyTask.ContinueWith(gkt =>
{
Task loginTask = Auth.SignInWithEmailAndPasswordAsync(gkt.Result.___.username, gkt.Result.____.password);
loginTask.ContinueWith(lt => { ....the next task.... });
});
return loginAfterGetKeyTask;
}
There are 2 problems I found :
As it goes on the code is getting deeper in the pyramid. Is there any way I can do to make the code looks more sequential?
This Login() function I want to return a Task so that caller can wait for the whole login process to finish then continue, etc. But currently the returned loginAfterGetKey task finished even if there are more things to do inside. I could keep doing .ContinueWith chaining on the outside if not for that there are one more Firebase task that is executed. Ideally I would like to return the Task that represent the whole thing but they are nested inside. Firebase's method start those task on it's own so I think I could not try to bundle all the task on my own.
I tried encapsulate all the tasks as a new Task start from Task.Factory.StartNew however "GetComponent can only be called from main thread" is a problem. (The Auth and other Firebase service would require GetComponent)
I tried using IEnumerator as a return of Login() and planned to put yield in this function, however, "The yield statement cannot be used inside anonymous method or lambda expression".
I tried using loginTask.Wait() inside the first ContinueWith in order to make loginAfterGetKeyTask that I return from this function not arrive at IsCompleted state until it reacehs the end (which would have to wait for the tasks inside) but since the lambda in this ContinueWith is in the main thread, it causes deadlock.
In the end I used IEnumerator loop waiting as a kind of hack to wait for the Task to finish. It 'linearize' the Task multi-thread system into single thread coroutine system that Unity seems to prefer. Each yield return null results in resuming in the next frame to check on the Task's status again.
Not very elegant but I could not find any better way outside async/await that pairs with Task but Unity's C# version does not yet support it.
public void Caller()
{
yield return StartCoroutine(Login());
// Login finished, check the login credentials
}
private IEnumerator WaitTask(Task task)
{
while (task.IsCompleted == false)
{
yield return null;
}
if(task.IsFaulted)
{
throw task.Exception;
}
}
public IEnumerator Login()
{
Task<DataSnapshot> getKeyTask = Database.Child("keyLocation").GetValueAsync();
yield return WaitTask(getKeyTask);
Task loginTask = Auth.SignInWithEmailAndPasswordAsync(getKeyTask.Result.___.username, getKeyTask.Result.____.password);
yield return WaitTask(loginTask);
//... the next task can use loginTask.Result etc.
}
It would be tricky still to catch exception since compiler does not allow try catch to wrap the yield. One way I might use the callback pattern to send the exception to the caller.
Here's an extension version in case you want to do like yield return task.YieldWait(); instead.
public static class TaskExtension
{
/// <summary>
/// Firebase Task might not play well with Unity's Coroutine workflow. You can now yield on the task with this.
/// </summary>
public static IEnumerator YieldWait(this Task task)
{
while (task.IsCompleted == false)
{
yield return null;
}
if(task.IsFaulted)
{
throw task.Exception;
}
}
}

Replacement for async void

I'm developing an application for monitoring certain tasks (e.g. if certain services/websites are currently up and running, certain records in the database exist, etc.). And as most these tasks are long running, I use TPL with async/await.
I have an base class for all such tasks:
public abstract class LongRunningOperation
{
// .. some props...
internal async void Start()
{
try
{
this.Status = OperationStatus.Started;
await this.DoStart();
this.Status = OperationStatus.Finished;
}
catch (Exception e)
{
this.Status = OperationStatus.Error;
this.Message = e.ToString();
}
}
protected abstract Task DoStart();
}
And method that launches these tasks looks like this:
public static LongRunningOperation[] LaunchOperations()
{
LongRunningOperation[] operations = GetAllLongRunningOperations();
foreach (var o in operations)
Task.Factory.StartNew(() => { o.Start(); });
return operations;
}
The array returned by this method is used to monitor all LongRunningOperations and log the stats. currently I have a console application having a while (true) loop that prints out the stats (name, status, current runtime) for each operation on the screen (refreshing every second) until all the operations are finished.
The thing that bothers me is the async void method. I've read that it's bad practice to use async void methods, but:
I can't figure out what harm they might do in my scenario
If I change the Start method to return Task, its return value will never be used anywhere, and I can't think why I need it
I'd appreciate it if someone could clarify these points
An async void method is a "fire and forget" operation. You can not wait for any result, and will not know when the operation completes and if it has been successful or not.
Basically you should use void when you are sure that you'll never need to know when the operation finished and if the operation execution was successful or not (for example writing logs).
With async methods that return Task, a caller is capable of waiting for an operation to finish, and also handle exceptions that happened during the execution of the operation.
To summarize, if you do not need a result, an async Task is slightly better because you can await it as well as handle exceptions and deal with task ordering.

Await stops execution of thread and never continues

I have the following:
public async Task<bool> SearchForUpdatesAsync()
{
return await TaskEx.Run(() =>
{
if (!ConnectionChecker.IsConnectionAvailable())
return false;
// Check for SSL and ignore it
ServicePointManager.ServerCertificateValidationCallback += delegate { return (true); };
var configurations = UpdateConfiguration.Download(UpdateConfigurationFileUri, Proxy);
var result = new UpdateResult(configurations, CurrentVersion,
IncludeAlpha, IncludeBeta);
if (!result.UpdatesFound)
return false;
_updateConfigurations = result.NewestConfigurations;
double updatePackageSize = 0;
foreach (var updateConfiguration in _updateConfigurations)
{
var newPackageSize = GetUpdatePackageSize(updateConfiguration.UpdatePackageUri);
if (newPackageSize == null)
throw new SizeCalculationException(_lp.PackageSizeCalculationExceptionText);
updatePackageSize += newPackageSize.Value;
_packageOperations.Add(new UpdateVersion(updateConfiguration.LiteralVersion),
updateConfiguration.Operations);
}
TotalSize = updatePackageSize;
return true;
});
}
As you can see I'm using Microsoft.Bcl.
Now in my other class I wrote this code in a normal void:
TaskEx.Run(async delegate
{
// ...
_updateAvailable = await _updateManager.SearchForUpdatesAsync();
MessageBox.Show("Test");
});
The problem I have is that it executes _updateAvailable = await _updateManager.SearchForUpdatesAsync(); and then it doesn't continue the thread, it just stops as if there is nothing after that call. Visual Studio also tells me this after a while: Thread ... exited with code 259, so something seems to be still alive.
I debugged through it to search for any exceptions that could maybe be swallowed, but nothing, everything works fine and it executes the return-statement.
And that is what I don't understand, I never see the MessageBox and/or no code beyond this line's being executed.
After I talked to some friends, they confirmed that this shouldn't be. Did I make a horrible mistake when implementing async-await?
Thanks in advance, that's actually all I can say about that, I got no more information, I appreciate any tips and help as far as it's possible.
The main issue that you're having is that you're unnecessarily wrapping your method in TaskEx.Run() and you are probably experiencing deadlock somewhere.
Your method signature is currently:
public async Task<bool> SearchForUpdatesAsync()
This means the following:
async --> Doesn't actually do anything, but provides a "heads up" that await might be called within this method and that this method can be used as a runnable Task.
Task --> This method returns a runnable task that can be run asynchronously on the threadpool
<bool> --> This method actually returns bool when awaited.
The await TaskEx.Run() is unnecessarily since this says run this method and then don't return until after a value is available. This is most likely causing a synchronization problem. Removing this construct will make your method work properly, however you'll notice that now you have no reason to even include the async operator or the Task<T> portion since the method is actually synchronous anyway. Usually you're only going to use async identifier on the method signature if you have methods that you are going to call await on them.
Instead you have two options.
Whenever you want to call SearchForUpdates() you can wrap this in a Task<bool>.Run() to run it asynchronously (or the Bcl equivalent)
Since you are using WinForms you might be better off using a BackgroundWorker and just calling this method within it.
Regarding using the async-await pattern I think that this is a great article to use to make sure you're following best practices: https://msdn.microsoft.com/en-us/magazine/jj991977.aspx
The best practice is to have async all the way through your layers, and then call await or less desirably .Wait() / .Result at the final use site.
Also, try to keep your UI calls separate from the backend work, since you can run into synchronicity/thread-context issue.
public class WinFormsCode
{
private async Task WinsFormCodeBehindMethodAsync()
{
var updatesAvailable = await _updateManager.SearchForUpdatesAsync();
MessageBox.Show("Updates Available: " + updatesAvailable.ToString());
}
private void WinsFormCodeBehindMethodSync()
{
var updatesAvailable = _updateManager.SearchForUpdatesAsync().Result;
MessageBox.Show("Updates Available: " + updatesAvailable.ToString());
}
}
public class UpdateManager
{
public async Task<bool> SearchForUpdatesAsync()
{
return true;
}
}

Loosely-ordered concurrency with loops?

I have the following code that is meant to fetch data from a REST service (the Get(i) calls), then populate a matrix (not shown; this happens within addLabels()) with their relationships.
All of the Get() calls can run in parallel to each other, but they must all be finished before anything enters the second loop (where, once again, the calls can run in parallel to each other). The addLabel() calls depend on the work from the Get() calls to be complete.
** To anyone stumbling across this post, this code is the solution:**
private async void GetTypeButton_Click(object sender, RoutedEventArgs e)
{
await PokeType.InitTypes(); // initializes relationships in the matrix
var table = PokeType.EffectivenessMatrix;
// pretty-printing the table
// ...
// ...
}
private static bool initialized = false;
public static async Task InitTypes()
{
if (initialized) return;
// await blocks until first batch is finished
await Task.WhenAll(Enumerable.Range(1, NUM_TYPES /* inclusive */).Select(i => Get(i)));
// doesn't need to be parallelized because it's quick work.
foreach(PokeType type in cachedTypes.Values)
{
JObject data = type.GetJsonFromCache();
addLabels(type, (JArray)data["super_effective"], Effectiveness.SuperEffectiveAgainst);
addLabels(type, (JArray)data["ineffective"], Effectiveness.NotVeryEffectiveAgainst);
addLabels(type, (JArray)data["no_effect"], Effectiveness.UselessAgainst);
}
initialized = true;
}
public static async Task<PokeType> Get(int id);
As the code is currently written, the InitTypes() method attempts to enter both loops simultaneously; the cachedTypes dictionary is empty because the first loop hasn't finished populating it yet, so it never runs and no relationships are constructed.
How can I properly structure this function? Thanks!
Parallel and async-await don't go together well. Your async lambda expression is actually async void since Parallel.For excpects an Action<int>, which means that Parallel.For can't wait for that operation to complete.
If you're trying to call Get(i) multiple times concurrently and wait for them to complete before moving on you need to use Task.WhenAll:
await Task.WhenAll(Enumerable.Range(1, NUM_TYPES).Select(() => Get(i)))

Calling async methods from a synchronous context

I'm calling a service over HTTP (ultimately using the HttpClient.SendAsync method) from within my code. This code is then called into from a WebAPI controller action. Mostly, it works fine (tests pass) but then when I deploy on say IIS, I experience a deadlock because caller of the async method call has been blocked and the continuation cannot proceed on that thread until it finishes (which it won't).
While I could make most of my methods async I don't feel as if I have a basic understanding of when I'd must do this.
For example, let's say I did make most of my methods async (since they ultimately call other async service methods) how would I then invoke the first async method of my program if I built say a message loop where I want some control of the degree of parallelism?
Since the HttpClient doesn't have any synchronous methods, what can I safely presume to do if I have an abstraction that isn't async aware? I've read about the ConfigureAwait(false) but I don't really understand what it does. It's strange to me that it's set after the async invocation. To me that feels as if a race waiting to happen... however unlikely...
WebAPI example:
public HttpResponseMessage Get()
{
var userContext = contextService.GetUserContext(); // <-- synchronous
return ...
}
// Some IUserContextService implementation
public IUserContext GetUserContext()
{
var httpClient = new HttpClient();
var result = httpClient.GetAsync(...).Result; // <-- I really don't care if this is asynchronous or not
return new HttpUserContext(result);
}
Message loop example:
var mq = new MessageQueue();
// we then run say 8 tasks that do this
for (;;)
{
var m = mq.Get();
var c = GetCommand(m);
c.InvokeAsync().Wait();
m.Delete();
}
When you have a message loop that allow things to happen in parallel and you have asynchronous methods, there's a opportunity to minimize latency. Basically, what I want to accomplish in this instance is to minimize latency and idle time. Though I'm actually unsure as to how to invoke into the command that's associated with the message that arrives off the queue.
To be more specific, if the command invocation needs to do service requests there's going to be latency in the invocation that could be used to get the next message. Stuff like that. I can totally do this simply by wrapping up things in queues and coordinating this myself but I'd like to see this work with just some async/await stuff.
While I could make most of my methods async I don't feel as if I have a basic understanding of when I'd must do this.
Start at the lowest level. It sounds like you've already got a start, but if you're looking for more at the lowest level, then the rule of thumb is anything I/O-based should be made async (e.g., HttpClient).
Then it's a matter of repeating the async infection. You want to use async methods, so you call them with await. So that method must be async. So all of its callers must use await, so they must also be async, etc.
how would I then invoke the first async method of my program if I built say a message loop where I want some control of the degree of parallelism?
It's easiest to put the framework in charge of this. E.g., you can just return a Task<T> from a WebAPI action, and the framework understands that. Similarly, UI applications have a message loop built-in that async will work naturally with.
If you have a situation where the framework doesn't understand Task or have a built-in message loop (usually a Console application or a Win32 service), you can use the AsyncContext type in my AsyncEx library. AsyncContext just installs a "main loop" (that is compatible with async) onto the current thread.
Since the HttpClient doesn't have any synchronous methods, what can I safely presume to do if I have an abstraction that isn't async aware?
The correct approach is to change the abstraction. Do not attempt to block on asynchronous code; I describe that common deadlock scenario in detail on my blog.
You change the abstraction by making it async-friendly. For example, change IUserContext IUserContextService.GetUserContext() to Task<IUserContext> IUserContextService.GetUserContextAsync().
I've read about the ConfigureAwait(false) but I don't really understand what it does. It's strange to me that it's set after the async invocation.
You may find my async intro helpful. I won't say much more about ConfigureAwait in this answer because I think it's not directly applicable to a good solution for this question (but I'm not saying it's bad; it actually should be used unless you can't use it).
Just bear in mind that async is an operator with precedence rules and all that. It feels magical at first, but it's really not so much. This code:
var result = await httpClient.GetAsync(url).ConfigureAwait(false);
is exactly the same as this code:
var asyncOperation = httpClient.GetAsync(url).ConfigureAwait(false);
var result = await asyncOperation;
There are usually no race conditions in async code because - even though the method is asynchronous - it is also sequential. The method can be paused at an await, and it will not be resumed until that await completes.
When you have a message loop that allow things to happen in parallel and you have asynchronous methods, there's a opportunity to minimize latency.
This is the second time you've mentioned a "message loop" "in parallel", but I think what you actually want is to have multiple (asynchronous) consumers working off the same queue, correct? That's easy enough to do with async (note that there is just a single message loop on a single thread in this example; when everything is async, that's usually all you need):
await tasks.WhenAll(ConsumerAsync(), ConsumerAsync(), ConsumerAsync());
async Task ConsumerAsync()
{
for (;;) // TODO: consider a CancellationToken for orderly shutdown
{
var m = await mq.ReceiveAsync();
var c = GetCommand(m);
await c.InvokeAsync();
m.Delete();
}
}
// Extension method
public static Task<Message> ReceiveAsync(this MessageQueue mq)
{
return Task<Message>.Factory.FromAsync(mq.BeginReceive, mq.EndReceive, null);
}
You'd probably also be interested in TPL Dataflow. Dataflow is a library that understands and works well with async code, and has nice parallel options built-in.
While I appreciate the insight from community members it's always difficult to express the intent of what I'm trying to do but tremendously helpful to get advice about circumstances surrounding the problem. With that, I eventually arrived that the following code.
public class AsyncOperatingContext
{
struct Continuation
{
private readonly SendOrPostCallback d;
private readonly object state;
public Continuation(SendOrPostCallback d, object state)
{
this.d = d;
this.state = state;
}
public void Run()
{
d(state);
}
}
class BlockingSynchronizationContext : SynchronizationContext
{
readonly BlockingCollection<Continuation> _workQueue;
public BlockingSynchronizationContext(BlockingCollection<Continuation> workQueue)
{
_workQueue = workQueue;
}
public override void Post(SendOrPostCallback d, object state)
{
_workQueue.TryAdd(new Continuation(d, state));
}
}
/// <summary>
/// Gets the recommended max degree of parallelism. (Your main program message loop could use this value.)
/// </summary>
public static int MaxDegreeOfParallelism { get { return Environment.ProcessorCount; } }
#region Helper methods
/// <summary>
/// Run an async task. This method will block execution (and use the calling thread as a worker thread) until the async task has completed.
/// </summary>
public static T Run<T>(Func<Task<T>> main, int degreeOfParallelism = 1)
{
var asyncOperatingContext = new AsyncOperatingContext();
asyncOperatingContext.DegreeOfParallelism = degreeOfParallelism;
return asyncOperatingContext.RunMain(main);
}
/// <summary>
/// Run an async task. This method will block execution (and use the calling thread as a worker thread) until the async task has completed.
/// </summary>
public static void Run(Func<Task> main, int degreeOfParallelism = 1)
{
var asyncOperatingContext = new AsyncOperatingContext();
asyncOperatingContext.DegreeOfParallelism = degreeOfParallelism;
asyncOperatingContext.RunMain(main);
}
#endregion
private readonly BlockingCollection<Continuation> _workQueue;
public int DegreeOfParallelism { get; set; }
public AsyncOperatingContext()
{
_workQueue = new BlockingCollection<Continuation>();
}
/// <summary>
/// Initialize the current thread's SynchronizationContext so that work is scheduled to run through this AsyncOperatingContext.
/// </summary>
protected void InitializeSynchronizationContext()
{
SynchronizationContext.SetSynchronizationContext(new BlockingSynchronizationContext(_workQueue));
}
protected void RunMessageLoop()
{
while (!_workQueue.IsCompleted)
{
Continuation continuation;
if (_workQueue.TryTake(out continuation, Timeout.Infinite))
{
continuation.Run();
}
}
}
protected T RunMain<T>(Func<Task<T>> main)
{
var degreeOfParallelism = DegreeOfParallelism;
if (!((1 <= degreeOfParallelism) & (degreeOfParallelism <= 5000))) // sanity check
{
throw new ArgumentOutOfRangeException("DegreeOfParallelism must be between 1 and 5000.", "DegreeOfParallelism");
}
var currentSynchronizationContext = SynchronizationContext.Current;
InitializeSynchronizationContext(); // must set SynchronizationContext before main() task is scheduled
var mainTask = main(); // schedule "main" task
mainTask.ContinueWith(task => _workQueue.CompleteAdding());
// for single threading we don't need worker threads so we don't use any
// otherwise (for increased parallelism) we simply launch X worker threads
if (degreeOfParallelism > 1)
{
for (int i = 1; i < degreeOfParallelism; i++)
{
ThreadPool.QueueUserWorkItem(_ => {
// do we really need to restore the SynchronizationContext here as well?
InitializeSynchronizationContext();
RunMessageLoop();
});
}
}
RunMessageLoop();
SynchronizationContext.SetSynchronizationContext(currentSynchronizationContext); // restore
return mainTask.Result;
}
protected void RunMain(Func<Task> main)
{
// The return value doesn't matter here
RunMain(async () => { await main(); return 0; });
}
}
This class is complete and it does a couple of things that I found difficult to grasp.
As general advice you should allow the TAP (task-based asynchronous) pattern to propagate through your code. This may imply quite a bit of refactoring (or redesign). Ideally you should be allowed to break this up into pieces and make progress as you work towards to overall goal of making your program more asynchronous.
Something that's inherently dangerous to do is to call asynchronous code callously in an synchronous fashion. By this we mean invoking the Wait or Result methods. These can lead to deadlocks. One way to work around something like that is to use the AsyncOperatingContext.Run method. It will use the current thread to run a message loop until the asynchronous call is complete. It will swap out whatever SynchronizationContext is associated with the current thread temporarily to do so.
Note: I don't know if this is enough, or if you are allowed to swap back the SynchronizationContext this way, assuming that you can, this should work. I've already been bitten by the ASP.NET deadlock issue and this could possibly function as a workaround.
Lastly, I found myself asking the question, what is the corresponding equivalent of Main(string[]) in an async context? Turns out that's the message loop.
What I've found is that there are two things that make out this async machinery.
SynchronizationContext.Post and the message loop. In my AsyncOperatingContext I provide a very simple message loop:
protected void RunMessageLoop()
{
while (!_workQueue.IsCompleted)
{
Continuation continuation;
if (_workQueue.TryTake(out continuation, Timeout.Infinite))
{
continuation.Run();
}
}
}
My SynchronizationContext.Post thus becomes:
public override void Post(SendOrPostCallback d, object state)
{
_workQueue.TryAdd(new Continuation(d, state));
}
And our entry point, basically the equivalent of an async main from synchronous context (simplified version from original source):
SynchronizationContext.SetSynchronizationContext(new BlockingSynchronizationContext(_workQueue));
var mainTask = main(); // schedule "main" task
mainTask.ContinueWith(task => _workQueue.CompleteAdding());
RunMessageLoop();
return mainTask.Result;
All of this is costly and we can't just go replace calls to async methods with this but it does allow us to rather quickly create the facilities required to keep writing async code where needed without having to deal with the whole program. It's also very clear from this implementation where the worker threads go and how the impact concurrency of your program.
I look at this and think to myself, yeap, that's how Node.js does it. Though JavaScript does not have this nice async/await language support that C# currently does.
As an added bonus, I have complete control of the degree of parallelism, and if I want, I can run my async tasks completely single threaded. Though, If I do so and call Wait or Result on any task, it will deadlock the program because it will block the only message loop available.

Categories