【问题标题】:universal windows, c#, await during app initialization通用窗口,c#,在应用程序初始化期间等待
【发布时间】:2016-10-18 21:09:24
【问题描述】:

我正在尝试将我在 Windows Phone 8 上运行的简单应用移植到通用 Windows。在构建主框架/页面之前,我需要(本地)加载一些文件,因为这些文件首先指示如何创建页面。有一个 .XML 和一些内容文件是资产的一部分。

我曾经在 App.Application_Launching() 期间进行初始化,现在我猜它已被 App.OnLaunched() 取代。问题在于新的仅异步文件 IO,我似乎无法找到一个可以在程序不挂起的情况下调用任何异步 API 的地方。看来App.OnLaunched()、MainPage.MainPage()、MainPage.OnNavigatedTo()等的任何地方都不能用await。

我基本上必须启动一个后台线程来安排一个任务稍后运行以进行实际初始化,然后回调到 MainPage 的池中以使某些东西正确运行。生成的代码如下所示。似乎过于复杂,但我想这是正确的?

谢谢, 尼克。

public partial class MainPage : Page
{
    // Constructor
    public MainPage()
    {
        InitializeComponent();

        // This doesn't work ...
        // var task = InitAsync();
        // task.Wait()


        // This seems to ...
        IAsyncAction asyncAction = ThreadPool.RunAsync((workItem) =>
        {
            var ignored = Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
            {
                TryInitAsync();
            });

        });

    }

    private async void TryInitAsync()
    {
        try
        {
            await InitAsync();
        }
        catch
        {
            int foo = 0;
        }
    }

    private async Task<Boolean> InitAsync()
    {
        // Things that go await in the night

        // Other things that update the layout

        return true;
    }
}

【问题讨论】:

    标签: c# asynchronous windows-10-universal


    【解决方案1】:

    似乎过于复杂,但我想这是正确的?

    不,我不会这么说。

    我无法提供有关初始化本身的具体建议,因为您的问题中缺少这些细节。基本策略是 UI 容忍未初始化的数据,并异步运行初始化,根据需要更新属性。

    就您启动异步初始化的具体方式而言,您已经使它变得比它需要或应该的更复杂。这样会更好:

    public MainPage()
    {
        InitializeComponent();
    
        var ignored = Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow
            .Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => TryInitAsync());
    }
    

    即如果您要做的只是推迟对 TryInitAsync() 方法的调用,并在 UI 线程上调用它,则无需使用 ThreadPool.RunAsync() 启动后台任务。

    以这种方式(或更复杂的方式)这样做,您仍然会阻塞 UI 线程。 使用Dispatcher.RunAsync() 会更好。相反,直接从后台任务调用TryInitAsync()(例如,使用ThreadPool.RunAsync() 调用它)并让依赖于初始化的UI绑定属性随着初始化的进行异步更新。

    【讨论】:

    • 啊,好吧,这就是我想要找到的。本质上是稍后在 UI 线程上安排的事情。
    • “稍后在 UI 线程上安排一些事情” -- 这就是调度程序 RunAsync() 方法将执行的操作。这不是最好的方法,因为它仍然会阻塞 UI 线程(就像直接从构造函数调用一样),但它可能对你有用。
    【解决方案2】:

    看来App.OnLaunched()、MainPage.MainPage()、MainPage.OnNavigatedTo()等的任何地方都不能使用await。

    核心概念变化是现代应用程序必须立即显示某种 UI(这意味着同步)。这就是你不能在启动时进行 I/O 的原因。

    因此,您必须考虑您的应用在加载任何文件之前应该是什么样子, 立即显示该状态,并开始异步加载您的“真实" 用户界面。

    你可以直接从你的构造函数调用TryInitAsync

    public MainPage()
    {
      InitializeComponent();
      TryInitAsync();
    }
    

    或者,如果您可以使用数据绑定更新您的 UI,您可以使用我的NotifyTask&lt;T&gt; type

    public NotifyTask<InitData> Data { get; }
    public MainPage()
    {
      InitializeComponent();
      Data = NotifyTask.Create(InitAsync());
    }
    private async Task<InitData> InitAsync() { ... }
    

    然后您可以将数据绑定到Data.Result 以获取InitData,或Data.IsCompleted/Data.IsNotCompleted 以显示/隐藏初始“正在加载...”状态。

    【讨论】:

    • 我最初尝试直接调用 InitAsync(),但使其同步会导致程序挂起(尽管有时如果在调试器中单步执行 if 会起作用)。是的,我确实有一个可以立即显示的默认布局,尽管目前它实际上只是“正在加载...”。我来看看 NotifyTask.
    • @user3799309:在我的代码中,InitAsync是直接调用的,但不是同步的。
    猜你喜欢
    • 2018-03-15
    • 2016-03-02
    • 1970-01-01
    • 1970-01-01
    • 2015-08-15
    • 1970-01-01
    • 1970-01-01
    • 2016-05-04
    • 1970-01-01
    相关资源
    最近更新 更多