Forum Xamarin.Forms

UI save task extension

Ok this isn't large or complex code, it is however effective. For folks that are having trouble getting async tasks to run without blowing up the UI here is an extension to Taskand Task<T> that will make your life very simple:

   using System;
    using System.Reactive.Linq;
    using System.Reactive.Threading.Tasks;
    using System.Threading;
    using System.Threading.Tasks;

    /// <summary>
    /// Add extenstion methods to Task
    /// where {T} is the type the Task returns.
    /// </summary>
    public static class TaskExtensions
    {

        /// <summary>
        /// Makes the Task{T} UI safe, asumming it is called on the UI thread
        /// </summary>
        /// <typeparam name="T">The type the task returns</typeparam>
        /// <param name="theTask">The executing or completed task.</param>
        /// <param name="onResults">Called for each {T} the task produces</param>
        /// <param name="onComplete">Optional: Called when there are no more {T}'s</param>
        /// <param name="onException">Optional: Called when the task faults</param>
        /// Element created at 11/11/2014,11:09 AM by Charles
        public static void UiTask<T>(this Task<T> theTask, Action<T> onResults, Action onComplete = null, Action<Exception> onException = null)
        {
            theTask.ToObservable().ObserveOn(SynchronizationContext.Current).Subscribe(onResults, onException, onComplete);    
        }

        /// <summary>Makes a void Task UI safe,assuming it is called on the UI thread</summary>
        /// <param name="theTask">The executing or completed task</param>
        /// <param name="onTaskComplete">Called whent the task has completed.</param>
        /// <param name="onException">Optional: Called when the task faults.</param>
        /// Element created at 11/11/2014,11:13 AM by Charles
        public static void UiTask(this Task theTask, Action onTaskComplete, Action<Exception> onException = null)
        {
            theTask.ToObservable().ObserveOn(SynchronizationContext.Current).Subscribe(_ => onTaskComplete(), onException, null);
        }


Example usage from on of my viewmodels:
//In the viewmodel ctor
 // Load our async data
            if (Class == null)
            {
                IsBusy = true;
                this.GetClassDefinitions()
                    .UiTask(
                        definitions =>
                            {
                                foreach (var d in definitions)
                                {
                                    AvailableClasses.Add(d);
                                }
                            },
                        () => IsBusy = false,
                        ex => Debug.WriteLine(ex.Message));
            }

Later in the class....

        /// <summary>
        /// Gets the class definitions.
        /// </summary>
        /// <returns>A Task that will return a enumeration of classdefintions in the future</returns>
        /// Element created at 11/11/2014,10:57 AM by Charles
        private Task<List<ClassDefinition>> GetClassDefinitions()
        {
            return Task.Run(async () =>
                    {
                        var classrep = new ClassRepository();
                        var definitions = await classrep.AvailableClasses();
                        return definitions;
                    });            
        }

Posts

  • adamkempadamkemp USInsider, Developer Group Leader mod

    Why can't you just do this?

    private async Task DoSomething()
    {
        if (Class == null)
        {
            IsBusy = true;
            try
            {
                var definitions = await this.GetClassDefinitions()
                foreach (var d in definitions)
                {
                    AvailableClasses.Add(d);
                }
            }
            catch (Exception e)
            {
                Debug.WriteLine(ex.Message));
            }
            finally
            {
                IsBusy = false,
            }
        }
    }
    
    private Task<List<ClassDefinition>> GetClassDefinitions()
    {
        return Task.Run(async () =>
                {
                    var classrep = new ClassRepository();
                    var definitions = await classrep.AvailableClasses();
                    return definitions;
                });
    }
    
  • CharlesHoranCharlesHoran USMember ✭✭

    @adamkemp‌
    In some circumstances you can.

    Your solution is completely viable if you are calling from an event handler which can be made async without complications. It is also viable if you are using something like Command that can be induced to have an async lambda.

    It is not however viable from a ctor which by definition cannot be async. You can start Tasks from a ctor and you can await within the task, however you cannot await within the ctor itself. Since you can start a task you have to marshall the task execution back onto the main thread in order to process the results to the ui. A lot of people are not familiar with those requirements and it is easy to introduce "What the heck" bugs that are hard to reproduce.

    Since this causes a certain amount of confusion within the community I choose to package up a successful usage pattern, a pattern that addresses all use cases that I am aware of into a class extension that is easy to use and ui thread safe.

    If you can think of use case that the extensions don't cover I'd love to hear about it. :D

  • adamkempadamkemp USInsider, Developer Group Leader mod

    In a constructor you can still do this:

    public MyClass()
    {
        DoSomething();
    }
    
    private async void DoSomething()
    {
        // Same code as before
    }
    

    I would still prefer this approach over a non-standard set of methods that effectively do the same thing, but in a way that isn't as easy to read.

  • powerdudepowerdude USMember ✭✭

    hey @charleshoran,

    you need to point out which nuget package you're using to get ToObservable. I believe it's the Microsoft Reactive Extensions, but i could be mistaken.

  • CharlesHoranCharlesHoran USMember ✭✭

    Yes you can do that async void's are the traditional firewall between async and sync code.

    However you cannot conveniently join the results of a method call like I can with observables.

    I'll agree that not everyone is comfortable with observables,which seems to be your case. However I will state that observables fit the all async all the time model for mobile development and to a lesser extent desktop development environments. The compostability that you get with observables simply cannot be reasonably met by more traditional software construction techniques.

  • CharlesHoranCharlesHoran USMember ✭✭

    @cliftonvaughn‌
    You are completely correct
    I use "Reactive Extension - Main Library" which pulls in the rest.

  • adamkempadamkemp USInsider, Developer Group Leader mod

    However you cannot conveniently join the results of a method call like I can with observables.

    You can. Just not in the constructor itself.

    I'm sure there are good use cases for observables. I just don't think this is an example where they're better than async. In my opinion the async code in this case is far more readable and far less error prone than the callbacks, and unless you have a case that can't be done with async then it's probably better to stick with async.

  • CharlesHoranCharlesHoran USMember ✭✭

    I suspect we will have to agree to disagree on this one. Especially the error prone part. Feel free to show how the task extension is more error prone than task based code. I'll wait :D

    I value the flexibility and the adaptability that observables provide. In the code snippet I am simply processing a single list of classroom definitions (think header information). Tomorrow or next week I might have to update that code to merget the classroom definition with third party reviews from some random source (perhaps a social media feed). With Observables I can simply merge the feeds and use projection to get the updated data.

    Much simpler than performing the same task with..err umm...tasks;

  • adamkempadamkemp USInsider, Developer Group Leader mod

    Any time you have to handle exceptions in a way other than try/catch it's error prone. For example, in your example code:

            if (Class == null)
            {
                IsBusy = true;
                this.GetClassDefinitions()
                    .UiTask(
                        definitions =>
                            {
                                foreach (var d in definitions)
                                {
                                    AvailableClasses.Add(d);
                                }
                            },
                        () => IsBusy = false,
                        ex => Debug.WriteLine(ex.Message));
            }
    

    Did you notice that if an exception is thrown from the task then you fail to set IsBusy back to false? As far as I can tell from documentation either the completed callback or the error callback will be called, but not both. In order to do that in your case you would have to do it like this:

            if (Class == null)
            {
                IsBusy = true;
                this.GetClassDefinitions()
                    .UiTask(
                        definitions =>
                            {
                                foreach (var d in definitions)
                                {
                                    AvailableClasses.Add(d);
                                }
                            },
                        () => IsBusy = false,
                        ex => { Debug.WriteLine(ex.Message); IsBusy = false; });
            }
    

    Now you have duplicate code in both the completion and error callbacks, which is not good. You wouldn't have that with a finally block, and it would be clearer by looking at the code that in the event of an exception anything after the exception won't execute. You can therefore see whether something will run in that case by just looking at where it is in the try/catch/finally sequence.

    It's issues like this that are the main reason async/await was implemented in the first place.

  • CharlesHoranCharlesHoran USMember ✭✭

    I'll have to have a peek to see what is actually happening under the covers.

    If your correct it is trivial to update the wrapper to call onComplete just before OnExection.

    That leaves the problem of exception recovery but that is the same in either model....

  • adamkempadamkemp USInsider, Developer Group Leader mod

    Maybe you should add a third callback that is equivalent to finally. Otherwise you have the problem of people putting code in the completed callback expecting it not to get called in the error case. It's confusing, and confusing means error prone.

    Of course once you add a third callback your code starts to look an awful lot like try/catch/finally, in which case you have to wonder why not just use try/catch/finally along with async/await? :)

  • CharlesHoranCharlesHoran USMember ✭✭

    Sorry adam,

    I have to disagree. Observables win based on flexibility and composability. Clearly you disagree. Now can you point to anything outside of your opinion that shows that Task based solutions are superior?

  • adamkempadamkemp USInsider, Developer Group Leader mod

    I don't disagree with that if you have a use case for composability. This just isn't an example of that. You're not composing anything. If you were composing these then maybe the fact that it's more error prone (which I don't think is really an opinion since it's one of the main reasons async/await was created in the first place) would be outweighed by the benefits of composability. Again, this just isn't one of those cases. The example you gave is a constructor. I showed a much simpler and more readable way to handle that use case, which also happens to be less error prone with exception handling. I think it's on you to show a real example where you're getting a benefit from this approach that outweighs its cost in readability and proneness to coder error.

    I'm not anti-Rx. I'm just anti-making-everything-callbacks-because-some-people-don't-understand-async-await. Use await where you can and use Rx where you need it. You don't have to go all one way or the other.

  • CharlesHoranCharlesHoran USMember ✭✭

    Async/Await was not created due to Rx boggle. Async await was created due to the complexity, and the wide variety, of existing asynchronous development models.

    If I was to pick a technology that really drove Task which lead to async await, it would be the TPL.

    And our disagreement is fairly fundamental here. You think the observable version is hard to read, I don't. You think that coding try/catch/finally blocks with thread affinity is easier than observables, I don't.

    I see equivalent code complexity for simple use cases. Sorry you won't convince me that it is harder to write a lambda than it is to write a finally block.

    I would argue that it is more natural for a developer to simply dot into an extension than to write a new method for what is 3-5 lines of code. It's the reason anonymous methods and lambdas took over event handlers....

    In addition I'll also argue that maintainability is enhanced by the simple fact that the maintainer does not have to search for the called method, it's right there. Yes I agree that if a handler is too large it should be factored into it's own method, at which point neither method has an advantage (imo).

    You can claim that it is easy to overlook the final lambda, I can point out that it is easy to not put in a finally block......they are the same level of complexity on simple use cases.

    I do suspect that our debate could reach angels on the head of a pin levels...so instead I'll simply say this is an approach that works, not everyone will like it or use it, which is fine :D

    As always Adam I enjoy our discussions and you did point out a flaw, so here is the updated code. Please note the removal of the onComplete parameter entirely. There is no logical reason to have both a onComplete and an onFinal.

       using System;
        using System.Reactive.Linq;
        using System.Reactive.Threading.Tasks;
        using System.Threading;
        using System.Threading.Tasks;
    
        /// <summary>
        /// Add extenstion methods to Task and Task{T}
        /// </summary>
        public static class TaskExtensions
        {
            /// <summary>
            /// The empty action used when the user does not supply a value(onFinal)
            /// and as a placeholder for the complete action
            /// </summary>
            /// Element created at 11/11/2014,6:08 PM by Charles
            private static readonly Action EmptyAction = () => { };
    
            /// <summary>The empty exception used when the user does not supply an onException</summary>
            /// Element created at 11/11/2014,6:08 PM by Charles
            private static readonly Action<Exception> EmptyException = ex => { };
    
            /// <summary>
            /// Makes the Task{T} UI safe, asumming it is called on the UI thread
            /// An alternate approach for simple use cases is :
            /// <code>
            /// <![CDATA[
            /// //ctor of a view model
            /// public MyViewModel()
            /// {
            ///     LoadSomeDataAsync();
            /// }
            /// 
            /// private async void LoadSOmeDataAsync()
            /// {
            ///     var results = await FetchMyData();
            ///     //Do something with the data.
            ///     //It is safe to update the ui at this point
            ///     //since await by default restores the same syncronization context
            ///     //as when it was invoked.
            /// }
            /// ]]>
            /// </code>        
            /// </summary>
            /// <typeparam name="T">The type the task returns</typeparam>
            /// <param name="theTask">The executing or completed task.</param>
            /// <param name="onResults">Called for each {T} the task produces</param>
            /// <param name="onFinal">Optional: Called when there are no more {T}'s or following onException, even if you did not supply an onException</param>
            /// <param name="onException">Optional: Called when the task faults</param>
            /// Element created at 11/11/2014,11:09 AM by Charles
            public static void UiTask<T>(this Task<T> theTask, Action<T> onResults, Action onFinal = null, Action<Exception> onException = null)
            {
                theTask.ToObservable().ObserveOn(SynchronizationContext.Current)
                    .Finally(onFinal ?? EmptyAction)
                    .Subscribe(onResults, onException ?? EmptyException,  EmptyAction);    
            }
    
            /// <summary>
            /// Makes a void Task UI safe,assuming it is called on the UI thread
            /// This method is not intended for "fire and forget" tasks.
            /// An alternate approach for simple use cases is :
            /// <code>
            /// <![CDATA[
            /// //ctor of a view model
            /// public MyViewModel()
            /// {
            ///     LoadSomeDataAsync();
            /// }
            /// 
            /// private async void LoadSOmeDataAsync()
            /// {
            ///     var results = await FetchMyData();
            ///     //Do something with the data.
            ///     //It is safe to update the ui at this point
            ///     //since await by default restores the same syncronization context
            ///     //as when it was invoked.
            /// }
            /// ]]>
            /// </code>        
            /// </summary>
            /// <param name="theTask"> The executing or completed task </param>
            /// <param name="onFinal"> Called when the task completes or after onException, even if you did not supply an onException. </param>
            /// <param name="onException"> Optional: Called when the task faults. </param>
            /// Element created at 11/11/2014,11:13 AM by Charles
            public static void UiTask(this Task theTask, Action onFinal,  Action<Exception> onException = null)
            {
                theTask.ToObservable().ObserveOn(SynchronizationContext.Current)
                    .Finally(onFinal ?? EmptyAction)
                    .Subscribe(_ => { }, onException ?? EmptyException, EmptyAction);
            }
        }
    
  • adamkempadamkemp USInsider, Developer Group Leader mod

    Async/Await was not created due to Rx boggle.

    I definitely did not say that. What I said was that async/await was created to avoid having to write callbacks in order to do asynchronous code. That's a fact. Here is a quote from Microsoft's own website describing why we need it (emphasis added):

    An improved version of ButtonClick may use tasks and task continuations to offload potentially long operations off the main thread. But it can also introduce another type of problem. Now the developer needs to think about synchronization with the main thread and in real-world application, when downloaded data may trigger other network requests, the method will require a cascade of tasks and continuations, making code hard to read.

    The value of async/await over using callbacks is much clearer when you end up chaining multiple asynchronous calls together. With lambdas you end up nesting lambdas further and further, and having separate error handlers for each level. Using async/await your code doesn't keep nesting. It reads just like synchronous code, and error handling can be put in a single catch block that handles exceptions from any of the asynchronous pieces. There is less redundancy and less room for leaving out important changes to state caused by that redundancy.

    This really is the reason Microsoft created the feature, and I agree with them. I have seen some great usages of Rx, and some cases where it really made UI code easier to understand, but I don't think it is a replacement for async/await for straightforward, statically defined chains of asynchronous code.

    I didn't intend for this to be a contentious issue. I feel like you read my comments as an indictment of Rx or something, which is not what I intended. I was just trying to discourage people reading this from thinking "if I'm doing asynchronous code then this is the way to do it", because I don't think this should be the first thing someone reaches for when writing asynchronous code. Maybe you disagree, and you'd rather use this instead of async/await, or maybe we're both just talking past each other (it wouldn't be the first time I've done that, sadly). I'll just leave it at that.

  • CharlesHoranCharlesHoran USMember ✭✭

    @adamkemp‌
    We are in agreement more than you suspect.

    I didn't take your comments as an indictment of any particular method or technique. Every technique has it's place (ok I take that back IAsyncResult is definitely out of my toolbox :D ).

    I work a with both observables and async/await quite a bit. Both have their place depending on the use case. For me when merging multiple future event streams together it is simpler to use Observables, your mileage will vary.

    For me in this is simply one technique of many that can work. I think that as requirements grow more complex observables pull ahead of the async/await model, especially when it comes to multiple event streams and maintaining a current state on them (ie you need to have 2 X and 1 Y received before starting process Z or you want to buffer the last 10 inputs for a batch process, or .....). If you use async/await for that type of a model you can run into deadlock issues fairly quickly while the observable model handles it with ease.

    For myself I tend to have async operations (web service calls/ database io) return observables(This is especially handy when working with SignalR :D ). I do this for two reasons:
    1) I'm not restricted in how I combine those future streams which makes me very adaptable to changing/evolving requirements.
    2) It leads to very natural async code. What I mean by that is that my viewmodel code isn't restricted to simply loading static data or some type of a polling interval to get fresh data. When fresh data arrives on a listener it simply appears in the viewmodel. This leads to simpler, thus more robust, overall designs. It is also why I value so highly observables ability to marshall the execution context to the correct synchronization context. I am far from certain that I could achieve SignalR processing in such a simple, natural manner without observables.

    Using nested lambda is almost always a bad idea, that is imo the modern equivalent to "goto" with an extra splash of who's on first for good measure. As soon as I see nested lamdbas I'll step back and take another look at the overall design. They are the design equivalent of sparrows in a mine.

    My intent is not to present observables as the one and only "true" way of managing async code. It is to present them as a viable option.

  • adamkempadamkemp USInsider, Developer Group Leader mod

    IAsyncResult is definitely out of my toolbox :D

    Ditto! :)

  • TektonTekton USMember ✭✭✭

    @CharlesHoran Works great, thanks. :smile:

Sign In or Register to comment.