Deekor
Deekor

Reputation: 9499

Wait for an async method to return

I have a class:

public class User
{
    public string id, name, email, image;

    public User (IFBGraphUser user)
    {
        id = user.GetId ();
        name = user.GetName ();
        GetEmail ();
    }


    private void GetEmail()
    {
        FBRequestConnection.StartWithGraphPath ("/me", null, "GET", ConnectionReturn);
    }

    private void ConnectionReturn(FBRequestConnection connection, NSObject result, NSError error)
    {
        var me = (FBGraphObject)result;
        Console.WriteLine("this is a test");
        this.email = me["email"].ToString();
    }
}

With a async method: StartWithGraphPath

When the constructor is called I want to wait for StartWithGraphPath to finish before GetEmail returns.

How Can I accomplish this?

StartWithGraphPath does not return an IAsyncResult so I can't use AsyncWaitHandle.

Edit

When the code is called:

User u = new User(user);
Console.WriteLine("hello");

My application output:

hello
this is a test

Which is what leads me to believe StartWithGraphPath is being called async. Is there another explanation?

Whats odd is there is also a method called StartWithGraphPathAsync So wouldn't this one Im using be synchronous by deduction? It has a synchronous feel while in debugger but not when simply running the app

Upvotes: 0

Views: 258

Answers (2)

Dirk
Dirk

Reputation: 10968

A quick solution would be to use a ManualResetEvent or ManualResetEventSlim, wait for it in the constructor and set it in the callback method:

private readonly ManualResetEventSlim myEvent;

public User(IFBGraphUser user)
{
    myEvent = new ManualResetEventSlim();
    id = user.GetId();
    name = user.GetName();
    GetEmail();

    myEvent.Wait();
}

private void ConnectionReturn(FBRequestConnection connection, NSObject result, NSError error)
{
    var me = (FBGraphObject)result;
    Console.WriteLine("this is a test");
    this.email = me["email"].ToString();

    myEvent.Set();
}

Please be aware that this solution only works if the StartWithGraphPath method is not using the thread that was used to call it to invoke the callback. If it does - e.g. classes close to the user interface often will execute callbacks on the UI thread - then a deadlock will occur. (Thanks to @L.B for pointing this out)

If you can modify your class design I would suggest removing the expensive call from the constructor. Constructing an object should usually be cheap, and using an asynchronous method in it suggests that it might take some time.

Upvotes: -1

Damien_The_Unbeliever
Damien_The_Unbeliever

Reputation: 239764

I wouldn't wait for it to complete in the constructor. Make the asynchrony visible (in a friendlier form) to the consuming code, and it can choose when or if to actually perform a wait for the email value:

public class User
{
    public string id, name, image;

    public User (IFBGraphUser user)
    {
        id = user.GetId ();
        name = user.GetName ();
        GetEmail ();
    }

    public Task<string> Email {
      get{
        return emailTask.Task;
      }
    }

    private TaskCompletionSource<string> emailTask =
         new TaskCompletionSource<string>();

    private void GetEmail()
    {
        FBRequestConnection.StartWithGraphPath ("/me", null, "GET", ConnectionReturn);
    }

    private void ConnectionReturn(FBRequestConnection connection, NSObject result, NSError error)
    {
        var me = (FBGraphObject)result;
        Console.WriteLine("this is a test");
        emailTask.SetResult(me["email"].ToString());
    }

}

Upvotes: 2

Related Questions