Reputation: 6723
This isn't so much a language specific issue, but for my particular case, I'm using C#. (Edit: Before reading further, the selected answer to this question is language specific to C#)
I have two asynchronous service calls that I need to complete and then collect the information from those two callbacks. Because of the nature of the code generators, I basically end up with methods that I call that have a "-Completed" event handler that gets called with the results of the service calls.
So, in this case, I have two separate async calls collecting data from two different sources. When they're both complete, I want to combine the data and then continue normal workflow.
So, in a C# style of pseudo-code:
MyAsyncServiceClient1 masc1 = new MyAsyncServiceClient1();
MyAsyncServiceClient2 masc2 = new MyAsyncServiceClient2();
masc1.MyCallCompleted += new EventHandler<MyCallCompletedEventArts>((sender, e) =>
{
.. collect some data ..
});
masc2.MyCallCompleted += new EventHandler<MyCallCompletedEventArts>((sender, e) =>
{
.. collect some more data ...
});
masc1.MyCall();
masc2.MyCall();
So my question is, is there a general pattern for consolidating asynchronous callbacks like this so that I can collect the data from both callbacks and then perform some action with the combined data? I mean, I know there are ways of doing this. I've devised ways in the past, but as I do more and more async programming, I find that this is becoming a common pattern and I want to see if I can find a more elegant, generalized solution than the custom solutions I continue to come up with.
Upvotes: 2
Views: 79
Reputation: 203823
What we can do is write a method to turn your event into a Task
. (Here is the method for one of your types; you'd need to create such a method for each of your events, unless both types implement an interface defining this event.) Having a task allows for composition, aggregate operations, etc. that make problems like this much easier to solve.
public static Task<MyCallCompletedEventArts> CallAsync(
this MyAsyncServiceClient1 client)
{
var tcs = new TaskCompletionSource<MyCallCompletedEventArts>();
EventHandler<MyCallCompletedEventArts> handler = null;
handler = (_, args) =>
{
tcs.TrySetResult(args);
client.MyCallCompleted -= handler;
};
client.MyCallCompleted += handler;
client.MyCall();
return tcs.Task;
}
(Note we jump through some hoops to make sure our handler is removed after the first time it fires. You can simplify the code a fair bit if that happens to be unnecessary in your case.)
Now you can write:
MyAsyncServiceClient1 masc1 = new MyAsyncServiceClient1();
MyAsyncServiceClient2 masc2 = new MyAsyncServiceClient2();
var results = await Task.WhenAll(masc1.CallAsync(), masc2.CallAsync());
DoSomethingWithFirstClientsArgs(results[0]);
Upvotes: 3