【问题标题】:The async method is not await异步方法不是等待
【发布时间】:2018-12-19 09:19:41
【问题描述】:

In this thread,我可以使用事件来解决我的问题,即在构造函数中等待异步方法。在这种情况下,事件使用不带参数的委托。

但现在我需要等待视图模型完成以等待异步方法可以继续。

我的主视图模型中有这段代码:

public void printMethod()
{
    SecondViewModel mySeocViewModel = new SecondViewModel(myParameter);
    SecondView mySecondView = new SecondView();
    mySecondView.DataContext = mySeocViewModel;

    //I have to wait until it finished to can print the user control
    //The problem is that this point is reached before mySecondViewModel finish.
}

我的第二个视图模型中的代码:

public docFacturasViewModel(MyType parameter)
{
    this.GetDataFromDatabaseEvent += OnGetDataFromDatabase;

    GetDataFromDatabaseEvent(parameter);
}


public delegate void GetDataFromDatabaseEventHandler(MyType parameter);
public event GetDataFromDatabaseEventHandler GetDataFromDataBaseEvent;

private async void OnBuscarDatos(MyType paramter)
{
    await getDataFromDatabaseAsync(parameter);

    //Fill the data of the properties of the view model with the data from database
}


private async Task getDataFromDatabaseAsync(MyType parameter)
{
    _myResult = (await getdataAsync(parameter)).FirstOrDefault();
}

如果我调试代码,它会以这种方式运行:

  • 它到达主视图模型中的行以创建第二个视图模型。
  • 在第二个视图模型中,它到达方法 OnGetDataFromDatabase 中的第一行,即 await。
  • 主视图模型继续执行代码,因此它不会等待来自数据库的结果。
  • 一段时间后,在第二个视图模型中,方法 OnGetDataFromDatabase 完成并在等待之后继续执行下一行代码。

我不明白为什么在第二个视图模型中代码不等到从数据库中获取数据,因为我使用 await 关键字并且所有代码都与帖子的情况相同我在开头链接。

谢谢。

编辑:

如果我使用第二个视图模型的构造函数:

Task.Run(() => buscarDatosAsync(paramFacturaParaImprimir)).Wait();

然后主视图模型等到第二个视图模型完成从数据库中获取数据。

但是当我在第一种情况下尝试这种方法时,在我链接并在另一篇文章中询问的情况下,该方法没有等待,所以我不明白为什么在一种情况下我必须使用委托而在另一种情况下,我可以使用一个任务并等待它完成。

【问题讨论】:

  • 可能是因为具有async/await 的方法的void 返回类型。没有minimal reproducible example 就很难说。
  • 只有实际的事件处理程序允许async void。但除此之外,基于当前提供的代码没有足够的信息。
  • 之前的链接建议是基于在创建对象时自动加载数据的要求,但假设没有其他类依赖于该操作。正如您所经历的那样,这意味着如果实际上存在依赖项,他们将无法等待该异步操作完成。如果打算等待,那么您需要删除先前建议的方法并公开一个有意等待的函数,该函数将由依赖对象调用/调用。
  • 另一种方法是创建一个任务完成源并让它监视异步操作,以便它可以等待直到它完成。
  • 真的不应该在构造函数中执行任何async / await 操作,这是非常危险的。无法可靠地判断对象何时完全构建是它所面临的最大问题之一。您应该认真考虑其他模式之一来异步初始化不依赖于构造函数的对象,例如async 工厂方法。有一些例子here。如果您绝对必须这样做,该链接为您提供了一种“更安全”的方式来处理它。

标签: c# async-await


【解决方案1】:

async void 方法到达await 时,控制权返回给调用者。显然,当您需要操作结果来继续处理时,这不是最好的主意。除此之外,我不希望构造函数具有创建即发即弃任务的副作用。

如果您需要将数据的结果作为对象构造的一部分,我建议您采用工厂方法:

private SecondViewModel()
{
}

public static async Task<SecondViewModel> CreateAsync(MyType parameter)
{
    var result = new SecondViewModel();

    result.SomeData = await getDataFromDatabaseAsync(parameter);

    return result;
}

你会这样使用它:

public async Task printMethod()
{
    SecondViewModel mySeocViewModel = await SecondViewModel.CreateAsync(myParameter);
    SecondView mySecondView = new SecondView();
    mySecondView.DataContext = mySeocViewModel;
}

【讨论】:

    【解决方案2】:

    假设您正在编写一些基于 XAML 的代码,那么您在这里遇到了设计问题。

    不要混合视图模型创建和数据加载。通常您会同步创建视图模型和视图,并以异步方式加载/发布数据。所以,不要在视图模型构造函数中等待。

    视图模型和视图必须在没有任何数据的情况下正常运行(当数据属性为nulls 或空集合时)。如果您不想在加载数据之前访问某些命令,只需禁用这些命令(从CanExecute 返回false)。

    【讨论】:

    • 感谢您的建议,但在这种情况下,我不确定。如果我必须从组合框的数据库项中加载,我应该从数据库的哪里加载这些数据?
    • 如果你的虚拟机是某种选择器,那么 start 在构造函数中加载(运行异步方法),但在构造函数中不要等待 .异步方法将:1)使用await从数据库中获取数据; 2)等待后填写适当的集合。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-19
    相关资源
    最近更新 更多