85

I know how to make Async methods but say I have a method that does a lot of work then returns a boolean value?

How do I return the boolean value on the callback?

Clarification:

public bool Foo(){
    Thread.Sleep(100000); // Do work
    return true;
}

I want to be able to make this asynchronous.

Overload119
  • 5,124
  • 5
  • 29
  • 34

6 Answers6

164

From C# 5.0, you can specify the method as

public async Task<bool> doAsyncOperation()
{
    // do work
    return true;
}

bool result = await doAsyncOperation();
Piero Alberto
  • 3,823
  • 6
  • 56
  • 108
Dave Arkell
  • 3,920
  • 2
  • 22
  • 27
  • 28
    For anyone else that cares, to get the results you'd use `bool result = await doAsyncOperation();` – Gordon Tucker Sep 16 '13 at 18:19
  • 4
    Whenever you are going to use the "await" keyword it needs to be inside a method that has been marked with, at minimum, "async". I originally though only the worker method had to be marked this way. For example even if your caller method will not return anything you have to name it "async void MyCallerMethod" – Mike K Oct 27 '14 at 16:52
  • Yeah but the pesky warning that the method will run synchronously – KingOfHypocrites Jun 18 '15 at 17:25
  • 8
    @KingOfHypocrites `return await Task.FromResult(true)` shall dismiss that warning. – Bojan Komazec Dec 12 '15 at 13:53
  • 2
    This won't actually do anything asynchronously unless you `await` something in the method body. Returning `Task.FromResult(true)` doesn't change that. The method body runs synchronously on the caller's thread up until the first await. – Drew Noakes Apr 28 '16 at 22:03
  • 2
    Looks to me like for this to work the "bool result = ..." line also has to be within an async method, so I don't think this really answers the question – nuander Nov 16 '16 at 22:46
  • @nuander Yes, the caller must also be asynchronous for this to work. And, every caller above that point as well. You can avoid this by using `Task.FromResult` directly, rather than using `await`. However, as [Drew noted earlier](https://stackoverflow.com/questions/6045343/how-to-make-an-asynchronous-method-return-a-value#comment61413553_6045445), this runs synchronously on the calling thread. – jpaugh May 21 '18 at 19:51
61

There are a few ways of doing that... the simplest is to have the async method also do the follow-on operation. Another popular approach is to pass in a callback, i.e.

void RunFooAsync(..., Action<bool> callback) {
     // do some stuff
     bool result = ...

     if(callback != null) callback(result);
}

Another approach would be to raise an event (with the result in the event-args data) when the async operation is complete.

Also, if you are using the TPL, you can use ContinueWith:

Task<bool> outerTask = ...;
outerTask.ContinueWith(task =>
{
    bool result = task.Result;
    // do something with that
});
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
4

Use a BackgroundWorker. It will allow you to get callbacks on completion and allow you to track progress. You can set the Result value on the event arguments to the resulting value.

    public void UseBackgroundWorker()
    {
        var worker = new BackgroundWorker();
        worker.DoWork += DoWork;
        worker.RunWorkerCompleted += WorkDone;
        worker.RunWorkerAsync("input");
    }

    public void DoWork(object sender, DoWorkEventArgs e)
    {
        e.Result = e.Argument.Equals("input");
        Thread.Sleep(1000);
    }

    public void WorkDone(object sender, RunWorkerCompletedEventArgs e)
    {
        var result = (bool) e.Result;
    }
NerdFury
  • 18,876
  • 5
  • 38
  • 41
  • A background worker is counter productive here. Don't you know AutoResetEvent/ManualResetEvent? – the_drow May 18 '11 at 13:25
  • 2
    @the_drow I disagree with that; a BackgroundWorker and the RunWorkerCompleted event are a perfectly good approach here – Marc Gravell May 18 '11 at 13:27
  • @the_drow - nope, I don't. I'll see what I can learn about it. But if you could explain why you think this is counter productive I'd like to understand. – NerdFury May 18 '11 at 13:37
  • It's counter productive since you are coding too much for something that doesn't really need that kind of code. A background worker is what you do when you have a long running process that reports to the UI. An async method in c# does not need to report progress, only completion as others might wait for it's completion (that's what Auto/MenualResetEvent are for). The async method pattern is a known idiom for asynchronously invoking a method. The only valid approaches here are what I suggested or what @Marc suggested. – the_drow May 18 '11 at 20:50
  • @MarcGravell: Not every thing is a nail. It may work. It's semantically incorrect. – the_drow May 18 '11 at 20:52
4

Probably the simplest way to do it is to create a delegate and then BeginInvoke, followed by a wait at some time in the future, and an EndInvoke.

public bool Foo(){
    Thread.Sleep(100000); // Do work
    return true;
}

public SomeMethod()
{
    var fooCaller = new Func<bool>(Foo);
    // Call the method asynchronously
    var asyncResult = fooCaller.BeginInvoke(null, null);

    // Potentially do other work while the asynchronous method is executing.

    // Finally, wait for result
    asyncResult.AsyncWaitHandle.WaitOne();
    bool fooResult = fooCaller.EndInvoke(asyncResult);

    Console.WriteLine("Foo returned {0}", fooResult);
}
Jim Mischel
  • 131,090
  • 20
  • 188
  • 351
  • The AsyncWaitHandle is a WaitHandle. It does not have a WaitOne() method. You must cast it down to whatever it holds in order to wait for completion. – the_drow May 18 '11 at 20:54
  • 1
    @the_drow: You might want to take a look at this example: http://msdn.microsoft.com/en-us/library/system.iasyncresult.asyncwaithandle.aspx. Also take a look at the documentation for the `WaitHandle.WaitOne` method: http://msdn.microsoft.com/en-us/library/58195swd.aspx – Jim Mischel May 18 '11 at 21:08
  • 2
    Or you could just paste that code into a C# program and test it. It seemed to work for me. – Jim Mischel May 18 '11 at 21:19
1

Perhaps you can try to BeginInvoke a delegate pointing to your method like so:



    delegate string SynchOperation(string value);

    class Program
    {
        static void Main(string[] args)
        {
            BeginTheSynchronousOperation(CallbackOperation, "my value");
            Console.ReadLine();
        }

        static void BeginTheSynchronousOperation(AsyncCallback callback, string value)
        {
            SynchOperation op = new SynchOperation(SynchronousOperation);
            op.BeginInvoke(value, callback, op);
        }

        static string SynchronousOperation(string value)
        {
            Thread.Sleep(10000);
            return value;
        }

        static void CallbackOperation(IAsyncResult result)
        {
            // get your delegate
            var ar = result.AsyncState as SynchOperation;
            // end invoke and get value
            var returned = ar.EndInvoke(result);

            Console.WriteLine(returned);
        }
    }

Then use the value in the method you sent as AsyncCallback to continue..

Omer Ben
  • 19
  • 1
-3

You should use the EndXXX of your async method to return the value. EndXXX should wait until there is a result using the IAsyncResult's WaitHandle and than return with the value.

the_drow
  • 18,571
  • 25
  • 126
  • 193