【问题标题】:Pattern for an async method that returns something immediately立即返回某些内容的异步方法的模式
【发布时间】:2014-02-18 20:17:22
【问题描述】:

如果我编写一个简单的函数,我可以立即得到结果。如果我使用async/await 并返回Task - 该方法会在任务完成后返回,但是如果我需要编写一个需要立即返回的方法,然后继续更新结果并可能最终完成任务?另外,如果我想在 WinRT 组件库之外公开它以便在其他语言的组件中使用怎么办?我将如何在 C# 中做到这一点,我将如何在 C++/CX 中做到这一点?还是 JS?

示例 1:

我想公开一个返回 ImageSource 的属性,以便我可以立即将它从我的 MVVM 视图模型绑定到 XAML 视图。加载ImageSource 的方法将位于一个单独的类中,该类公开在 WinRT 组件之外(它是一个公共方法)。现在我希望该方法可以等待,或者至少以某种方式返回我可以等待的任务,但也立即返回ImageSource,因此我调用它的属性可以立即返回,因为属性不能是异步的。调用者不知道ImageSource 将是什么类型,因此它无法实例化它,因为ImageSource 实际上是一种抽象类型,通常表示为BitmapImageWriteableBitmap,在我的情况下,两者都可以从方法。显然,该方法本身会立即知道它是否会返回任何一种类型的对象,但是读取/创建和/或解码图像需要一些时间。

我在想 C# 中的签名可能是这样的

public async Task<ImageSource> GetImage(
    object key,
    out ImageSource bitmap,
    CancellationToken cancellationToken)

我根本不会在属性访问器中等待方法的结果,但我想我能够立即返回位图参数,而当在我的视图模型的代码中的其他地方调用或事件时我将能够等待或取消任务。

示例 2:

我希望能够列出磁盘中的文件并在列出所有文件后获得完成的任务,但立即返回视图模型的 IObservableVector 表示用于在我的 XAML UI 中更新为页面的文件文件是异步加载的。

在这里我可能会做类似的事情:

public async Task<int> GetImages(
    object queryParemeters,
    out ObservableCollection<CustomFileInfoType> files,
    CancellationToken cancellationToken)

问题

现在上面看起来差不多了,但我不认为我可以在 WinRT 组件之外公开 TPL 任务,因为 Task 不是 WinRT 类型,所以我可能会给出一个像上面这样的内部方法和一个通过调用AsyncInfo.Run(),将结果包装为IAsyncOperation,传递任务和取消令牌。 ObservableCollection 也只是 .NET,所以我可能需要围绕它创建一个实现 IObservableVector 的包装器,因为我认为 .NET 中没有可用的包装器。这些可能还有其他潜在问题,我不确定这种设计是否正确。

然后 - 我将如何在 C++/CX 中完成所有这些工作?还是JS?

【问题讨论】:

    标签: c# windows-runtime async-await c++-cx winrt-component


    【解决方案1】:

    async 建立在异步操作的概念之上,具有明确的开始和结束。最后,可能只有一个结果。而已。请注意,async 方法可能没有 out 参数,因为它们不适合此模型。

    如果您想要一个值流,请使用Reactive Extensions。有一个有趣的RxUI library 很好地将可观察对象与 MVVM 模式结合起来。

    也就是说,我认为您的任何一个示例实际上都不需要 observables(尽管如果您愿意,您当然可以 移至 Rx)。我解决您的第一个示例(数据绑定async 属性)on my blog;简短的回答是使用实现INotifyPropertyChanged like this oneTask&lt;T&gt; 的包装器:

    // Service
    public async Task<ImageSource> GetImage(object key, CancellationToken cancellationToken);
    
    // ViewModel
    INotifyTaskCompletion<ImageSource> Image { get; private set; }
    ...
    Image = NotifyTaskCompletion.Create(GetImage(key, token));
    
    // View
    <Image Source="{Binding Image.Result}" />
    

    关于您的第二个示例,这可以通过将新项目视为来自async 方法的进度更新来相当容易地完成:

    // Service
    public async Task<int> GetImages(object queryParemeters,
        CancellationToken cancellationToken,
        IProgress<CustomFileInfoType> progress);
    
    // ViewModel
    var collection = new ObservableCollection<CustomFileInfoType>();
    var progress = new Progress<CustomFileInfoType>(x => collection.Add(x));
    await GetImages(query, token, progress);
    

    公开这些类型完全是另一回事。 WinRT 组件必须公开 WinRT 类型。我建议您使用纯 async/await 编写基本逻辑(服务和可能的 ViewModel),然后单独进行翻译。正如您所指出的,AsyncInfo.Run 会将Task 翻译成IAsyncOperation,并且没有将ObservableCollection 翻译成IObservableVector 的内置翻译器(虽然它并不难编写,并且有几个可用的通过谷歌)。

    然后 - 我将如何在 C++/CX 中完成所有这些工作?还是JS?

    我不知道那个。您可能必须在这些平台上为 NotifyTaskCompletion 编写自己的等效项,或者只使用回调。

    【讨论】:

    • 您的NotifyTaskCompletion 看起来像是汇编内部代码的有趣解决方案。我可能需要实现一些可以跨 ABI 工作的东西,所以我需要编写一个与 WinRT 兼容的版本,希望这没什么大不了的。 Progress 的跨 ABI 版本可能是一个更大的问题。我想我需要挖掘一些关于在 WinRT 组件之间传递和执行回调的说明。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-11-15
    • 1970-01-01
    • 2019-06-03
    • 2018-03-03
    • 1970-01-01
    • 2014-05-08
    • 1970-01-01
    相关资源
    最近更新 更多