2

Having my first crack at writing a TAP asynchronous implementation of a simple command-response style comms. protocol stack.

The reference synchronous implementation provides a couple of public methods

public E SendCommand(C command);
public E SendCommandAndRetrieveResponse(C command, out R response);

(where E is some form of error code type) that both invoke a common internal

E _SendCommandAndRetrieveResponse(C command, out R response);

passing a dummy response in the case of SendCommand().

So I'm thinking that the signature of of the equivalent asynchronous methods would be

public Task<E> SendCommandAsync(C command);
public Task<Tuple<E, R>> SendCommandAndRetrieveResponseAsync(C command);

So far so good, but whats got me stumped is if I follow the synchronous model and use a common private

Task<Tuple<E, R>> _SendCommandAndRetrieveResponseAsync(C command);

how do I convert/proxy the Task<Tuple<E, R>> returned by the private method into a Task<E> for the benefit of SendCommandAsync()?

thx

Richard.

Richard Lang
  • 456
  • 4
  • 15

3 Answers3

4

Just make it async.

public async Task<E> SendCommandAsync(C command)
{
    var result = await _SendCommandAndRetrieveResponseAsync(command);
    return result.Item1;
}
Jeff Mercado
  • 129,526
  • 32
  • 251
  • 272
1

Since the Tuple has two values, you can simply return the first one from the common private, which is E. This value can be used as a result for Task.FromResult, which will create a completed task based on the value.

public Task<E> SendCommandAsync(C command)
{
    return Task.FromResult(_SendCommandAndRetrieveResponseAsync(command).Result.Item1);
}

Note: This solution uses .NET 4.5 features, see this question for alternate solutions instead of FromResult.

Community
  • 1
  • 1
Cyral
  • 13,999
  • 6
  • 50
  • 90
  • Will that work? The value you are returning is `E` and not `Task` in that scenario isn't it? – Johnathon Sullinger Jul 02 '15 at 02:49
  • @JohnathonSullinger Your right, I'll have to make an example and try and solve this. – Cyral Jul 02 '15 at 02:50
  • I believe this should be `FromResult(_SendCommandAnd..(command).Result.Item1)`. You can't access `Item1` directly there. – Johnathon Sullinger Jul 02 '15 at 02:59
  • @JohnathonSullinger Nice catch, fixed. – Cyral Jul 02 '15 at 02:59
  • Isn't this implementing SendCommandAsync() as a synchronous blocking call that returns a completed task once the task returned by the underlying _SendCommandAndRetrieveResponseAsync() call has completed? Not what I was angling for. – Richard Lang Jul 02 '15 at 03:17
  • I believe in this case the answer is yes as it has to wait for the first task to complete before it can put the result into `Task.FromResult`. – Johnathon Sullinger Jul 02 '15 at 03:33
  • If that is what you're after why don't you just use async/await as in Jeff's answer? – Cyral Jul 02 '15 at 04:10
  • This won't work as you intend it to. By accessing the `Result` property of the task, you're effectively making the call synchronous since it will block to wait for the result. If you're going to process the task, you need to use a continuation. – Jeff Mercado Jul 02 '15 at 14:59
0

You can use a ContinueWith call to convert the Task holding the Tuple, into a Task holding just E. This has a side-effect of creating a nested task Task<Task<E>>, which can be solved by unwrapping them.

private Task<Tuple<E, R>> _SendCommandAndRetrieveResponseAsync(C command)
{
    Task<Tuple<E, R>> result = //.....

    return result;
}

public Task<E> SendCommandAsync(C command)
{
    return _SendCommandAndRetrieveResponseAsync(command)
        .ContinueWith(task => new TaskCompletionSource<E>(task.Result.Item1).Task)
        .Unwrap();
}
Johnathon Sullinger
  • 7,097
  • 5
  • 37
  • 102
  • I *think* this is what I'm after, but won't have my code in a state to be able to try it out for a while yet. Will be back to vote once I've seen it working. thx – Richard Lang Jul 02 '15 at 03:29