Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it true that deferral should be added for any async operation?

According to Likness (p. 164, "Building Windows 8 Apps with C# and XAML"), "When performing asynchronous tasks, you must ask for a deferral."

So if I'm not taking him out of context, this code:

private async Task<System.Collections.Generic.KeyValuePair<string, string>> SelectAContactForASlot()
{
    KeyValuePair<string, string> kvp; 
    var contactPicker = new Windows.ApplicationModel.Contacts.ContactPicker();
    contactPicker.CommitButtonText = "Select";
    var contact = await contactPicker.PickSingleContactAsync();
    if (contact != null)
    {
        kvp = new KeyValuePair<string, string>(contact.Name, contact.Emails[0].ToString());
        return kvp;
    }
    return kvp = new KeyValuePair<string, string>("No Name found", "No email found");
}

...should be this instead:

private async Task<System.Collections.Generic.KeyValuePair<string, string>> SelectAContactForASlot()
{
    var deferral = e.SuspendingOperation.GetDeferral();
    KeyValuePair<string, string> kvp; 
    var contactPicker = new Windows.ApplicationModel.Contacts.ContactPicker();
    contactPicker.CommitButtonText = "Select";
    var contact = await contactPicker.PickSingleContactAsync();
    if (contact != null)
    {
        kvp = new KeyValuePair<string, string>(contact.Name, contact.Emails[0].ToString());
        return kvp;
    }
    return kvp = new KeyValuePair<string, string>("No Name found", "No email found");
    deferral.Complete();
}

Correct?

like image 488
B. Clay Shannon-B. Crow Raven Avatar asked Nov 16 '12 17:11

B. Clay Shannon-B. Crow Raven


1 Answers

Remember that an async method returns when it hits an await and has to (asynchronously) wait.

You need a deferral when you await an operation from an async void method that must complete before it returns. This kind of "event" is really a command. E.g., "the mouse moved" is just an event - the system doesn't care whether or not you handle the event. But "suspend" is a command - the system assumes that when you return from the command, you are done getting ready to be suspended. In this case, deferrals are necessary to inform the system that even though you're returning, you're not done yet.

Similarly, you need a deferral if you have a background task that has an async implementation of Run. Because when Run returns, your background task is considered done, and you need a way to say you're not really done yet.

You can tell if an event handler supports this by the presence of a GetDeferral method. For example, Suspending supports deferral because SuspendingEventArgs has a SuspendingOperation property which has a GetDeferral method. In the background task scenario (i.e., you have an async void Run), you can call GetDeferral on the IBackgroundTaskInstance passed into Run.

Your example of SelectAContactForASlot returns Task, so it doesn't need the deferral.

like image 89
Stephen Cleary Avatar answered Nov 19 '22 11:11

Stephen Cleary