使用 Task.FromResult 创建围绕同步函数的异步包装器

本文关键字:函数 异步 包装 同步 Task FromResult 创建 使用 | 更新日期: 2023-09-27 18:32:43

我正在学习 asp.net vnext系列的教程。我在教程中遇到了一些没有多大意义的东西:

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;
        }
    }
}

他们正在为同步方法创建一个包装器,并仅调用Task.FromResults()以便其异步。首先,它仍然会阻塞,那么有什么意义呢?可能在幕后,它只是创建一个TaskCompletionSource并将结果包装Task对象中。然后他们await结果,这可能只是在产生我认为不需要的额外开销。他们通过 EF 进行的数据库调用仍处于同步状态。我在这里遗漏了什么还是一个不好的例子?

使用 Task.FromResult 创建围绕同步函数的异步包装器

这是一个不好的例子。围绕同步操作创建异步包装器没有任何价值,这样的决定通常应该留给库使用者。

我相信唯一应该公开的异步方法是那些比同步对应方法具有可扩展性优势的方法。 异步方法不应该纯粹为了卸载而公开:同步方法的使用者可以很容易地实现这种好处

从 我应该为同步方法公开异步包装器吗?

虽然有时它是合适的(例如遵守Task返回接口或抽象方法(,但情况似乎并非如此。

你说得很对,它最终会阻塞,并且让所有东西都有一个异步 API 但具有同步行为只会表现得更糟。

至于你为什么要这样做;你会这样做,以便它向实际的异步版本公开相同的API,允许两个实现满足接口或其他类型的协定,允许它们被不知道实际使用哪个实现的调用者使用。 当应用程序过渡到完全异步时,它可能在这里被用作权宜之计,但某些部分还没有异步实现。 当您升级数据库提供程序并具有这些方法的实际异步版本时,这意味着您只需要更改此实现,并且不需要更改调用方方面的任何内容。

当然,如果你最终有一个调用者期望这样的方法实际上异步运行并且长时间不阻塞,因为它根本没有这样做,那么它有相当多的潜在问题。

这是一个黑客;不是没有目的的,但你确实需要认识到它是这样的。

我必须同意 I3arnon 的观点:它没有任何用处,只能混淆这段代码的使用者。

该示例没有实现任何通用接口,因此 Servy 关于公开相同 API 的论点在这种情况下感觉不对。

方法存根:

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

明确声明它不仅返回一个任务(这并不一定意味着异步性(,而且顾名思义,它是异步的。

至于增加的开销,此处执行的唯一不必要的操作是创建 Task 对象。不执行异步/TPL代码。为此,我会说这个例子只是误导。

如果这是我的代码,我会假设我一直很着急,并期望看到一个

// TODO: Make this async!

该方法中的注释;)