简体   繁体   中英

using Task.FromResult to create async wrappers around sync functions?

i'm going through a tutorial in the asp.net vnext series. I came across something in the tutorial that doesn't make a lot of sense:

using System.Linq;
using Microsoft.AspNet.Mvc;
using TodoList.Models;
using System.Threading.Tasks;

namespace TodoList.ViewComponents
{
    public class PriorityListViewComponent : ViewComponent
    {
        private readonly ApplicationDbContext db;

        public PriorityListViewComponent(ApplicationDbContext context)
        {
            db = context;
        }

        // Synchronous Invoke removed.

        public async Task<IViewComponentResult> InvokeAsync(int maxPriority, bool isDone)
        {
            string MyView = "Default";

            // If asking for all completed tasks, render with the "PVC" view.
            if (maxPriority > 3 && isDone == true)
            {
                MyView = "PVC";
            }

            var items = await GetItemsAsync(maxPriority, isDone);

            return View(MyView, items);
        }

        private Task<IQueryable<TodoItem>> GetItemsAsync(int maxPriority, bool isDone)
        {
            return Task.FromResult(GetItems(maxPriority, isDone));

        }
        private IQueryable<TodoItem> GetItems(int maxPriority, bool isDone)
        {
            var items = db.TodoItems.Where(x => x.IsDone == isDone &&
                                                x.Priority <= maxPriority);

            string msg = "Priority <= " + maxPriority.ToString() +
                         " && isDone == " + isDone.ToString();
            ViewBag.PriorityMessage = msg;

            return items;
        }

    }
}

They are creating a wrapper for a sync method and just calling Task.FromResults() so its async. First of all its still going to block so whats the point? Probably behind the scenes its just creating a TaskCompletionSource & wrapping the results in Task object. They then await the results which probably just is creating extra overhead that is not needed i would think. The db call they are making through EF is still sync. Am i missing something here or is a bad example?

It's a bad example. It serves no value to create an async wrapper around a synchronous operation and such a decision should usually be left to the library consumer.

I believe the only asynchronous methods that should be exposed are those that have scalability benefits over their synchronous counterparts. Asynchronous methods should not be exposed purely for the purpose of offloading: such benefits can easily be achieved by the consumer of synchronous methods

From Should I expose asynchronous wrappers for synchronous methods?

And while there are times when it's appropriate (like complying with a Task returning interface or abstract method) this doesn't seem to be the case.

You're quite right that it's just going to end up blocking, and that making everything have an asynchronous API but with synchronous behavior is only going to perform [slightly] worse.

As for why you'd do it; you'd do it so that it exposes an identical API to the actually asynchronous versions, allowing both implementations to meet an interface or other type of contract allowing them to be used by a caller that doesn't know which implementation is actually being used. It is likely being used here as a stop gap while the application is transitioning to being entirely asynchronous, but to which certain pieces do not yet have asynchronous implementations. When you upgrade your database provider and have actually asynchronous versions of these methods it'll mean you only need to change this implementation, and won't need to change anything on the caller's side of things.

Of course, it has its fair share of potential problems if you end up with a caller expecting a method like this to actually behave asynchronously and not block for a long period of time, given that it doesn't do that at all.

This is a hack; not one without purpose, but you do need to recognize it as such.

I have to agree with I3arnon: It serves no purpose but to confuse the consumer of this code.

The example isn't implementing any common interface, so Servy's argument about exposing an identical API doesn't feel right in this case.

The method stub:

private Task<IQueryable<TodoItem>> GetItemsAsync(int maxPriority, bool isDone) { .. }

explicitly declares that it not only returns a task (which does not necessarily imply asynchronicity), but by its very name, that it is async.

As for increased overhead, the only unnecessary action that is performed here is the creation of a Task object. No async / TPL code is executed. To that end, I'd say this example is simply misleading.

If this was my code, I'd assume I had been in a rush, and expect to see a

// TODO: Make this async!

comment in that method ;)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM