How to create an IAsyncResult that immediately completes?
Asked Answered
R

4

10

I am implementing an interface which requires implementations of BeginDoSomething and EndDoSomething methods. However my DoSomething isn't really long-running. For simplicity assume DoSomething only compares two variables and return whether a > b

So my BeginDoSomething should be like:

protected override IAsyncResult BeginDoSomething(int a, int b, AsyncCallback callback, object state)
{
     bool returnValue = a > b;
     return ...; //what should I return here?  
     //The method actually already completed and I don't need to wait for anything
 }

I don't know what I should return. I only implement BeginDoSomething because I have to, not because my method is long-running. Do I need to implement my own IAsyncResult? Is there an implementation already in .NET libraries?

Rhizo answered 18/2, 2011 at 3:50 Comment(0)
B
6

The quick hack way of doing it is to use a delegate:

protected override IAsyncResult BeginDoSomething(int a, int b, AsyncCallback callback, object state)
{
     bool returnValue = a > b;
     Func<int,int,bool> func = (x,y) => x > y;
     return func.BeginInvoke(a,b,callback,state);
}

The downside of this approach, is that you need to be careful if two threads will be calling this method concurrently you'll get an error.

Berty answered 18/2, 2011 at 4:14 Comment(4)
Not actually quick but hard to argue with the simplicity. Omit returnValue. I don't see the error.Galimatias
what should be in the EndDoSomething then? I'm sorry I am very new on asynchronous programmingRhizo
in End* it would be the same as func.BeginInvoke : IAsyncResult, so you are in effect delegating the responsibility to Delegate's implementation.Sungsungari
func.EndInvoke? but func is only declared in Begin* method, it can't be called in End*Rhizo
S
6

This is a little quick and dirty, but you can implement a class that implements IAsyncResult like so:

    public class MyAsyncResult : IAsyncResult
    {
        bool _result;

        public MyAsyncResult(bool result)
        {
            _result = result;
        }

        public bool IsCompleted
        {
            get { return true; }
        }

        public WaitHandle AsyncWaitHandle
        {
            get { throw new NotImplementedException(); }
        }

        public object AsyncState
        {
            get { return _result; }
        }

        public bool CompletedSynchronously
        {
            get { return true; }
        }
    }

Then use it in your BeginDoSomething like this:

    return new MyAsyncResult(a > b);
Stradivarius answered 18/2, 2011 at 4:8 Comment(4)
I think the AsyncWaitHandle property should return a reference to a set ManualResetEvent (or similar). It's perfectly reasonable for clients to try to wait on the handle, and that shouldn't cause an exception to be thrown.Dinodinoflagellate
True, we could do that, but as a WaitHandle is an expensive object, it seems wasteful to create one when it will never be relevant.Stradivarius
The code is missing the callback call, required (if not null).Galimatias
Aww, ⁺¹, one have to implement only getters… It wan't obvious, I couldn't understand why public bool IsCompleted keeps be mentioned as not implemented.Jam
B
6

The quick hack way of doing it is to use a delegate:

protected override IAsyncResult BeginDoSomething(int a, int b, AsyncCallback callback, object state)
{
     bool returnValue = a > b;
     Func<int,int,bool> func = (x,y) => x > y;
     return func.BeginInvoke(a,b,callback,state);
}

The downside of this approach, is that you need to be careful if two threads will be calling this method concurrently you'll get an error.

Berty answered 18/2, 2011 at 4:14 Comment(4)
Not actually quick but hard to argue with the simplicity. Omit returnValue. I don't see the error.Galimatias
what should be in the EndDoSomething then? I'm sorry I am very new on asynchronous programmingRhizo
in End* it would be the same as func.BeginInvoke : IAsyncResult, so you are in effect delegating the responsibility to Delegate's implementation.Sungsungari
func.EndInvoke? but func is only declared in Begin* method, it can't be called in End*Rhizo
W
2

I am not sure if I am missing something but today you can just return Task.CompletedTask/Task.FromResult. Task implements IAsyncResult

protected override IAsyncResult BeginDoSomething(int a, int b, AsyncCallback callback, object state)
{
     return Task.FromResult(a > b);
}

The IAsyncResult.IsCompleted is rightly true here. The Func.BeginInvoke approach wouldn't result in true.

Wombat answered 15/7, 2020 at 1:32 Comment(0)
S
1

I'd suggest you follow the directions here to be able to create your implementations using Task based methods in case one of them does turn out to be long running. If DoSomething is short running, you can block on it by creating a Task from its result

public IAsyncResult BeginDoSomething(int a, int b,
                                        AsyncCallback callback,
                                        object state)
{
   return Task.FromResult(DoSomething(a, b)).AsApm(callback, state);
}

public bool EndDoSomething(IAsyncResult asyncResult)
{
   return ((Task<bool>)asyncResult).Result;
}

bool DoSomething(int a, int b)
{
   return a > b;
}

public static IAsyncResult AsApm<T>(this Task<T> task,
                                    AsyncCallback callback,
                                    object state)
{
    if (task == null)
        throw new ArgumentNullException("task");

    var tcs = new TaskCompletionSource<T>(state);
    task.ContinueWith(t =>
                      {
                         if (t.IsFaulted)
                            tcs.TrySetException(t.Exception.InnerExceptions);
                         else if (t.IsCanceled)
                            tcs.TrySetCanceled();
                         else
                            tcs.TrySetResult(t.Result);

                         if (callback != null)
                            callback(tcs.Task);
                      }, TaskScheduler.Default);
    return tcs.Task;
}
Stanford answered 20/10, 2020 at 21:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.