xamarin 如何等待来自命令的任务?

ybzsozfc  于 2023-01-28  发布在  其他
关注(0)|答案(1)|浏览(109)

我正在尝试等待任务结束。此任务从服务器加载一系列内容,通常需要2到3秒。此任务由命令LoadItemsCommand启动。任务结束后,它应该检查是否加载了任何内容,以及加载的项目是否已经包含值。如果是,它应该从工具栏中删除一个按钮。
但它没有等待。命令调用并特别要求等待任务,但在主页面上,事情没有等待任务结束就继续进行。我试图放置一个30秒等待时间的循环来等待命令工作,但没有任何效果。
下面是视图模型中的命令:

LoadItemsCommand = new Command(async () => await ExecuteLoadItemsCommand());

下面是ViewModel中的LoadItems函数

async Task ExecuteLoadItemsCommand()
{
    if (IsBusy)
        return;
    IsBusy = true;

    List<string> stockIds = new List<string>();
    foreach (var orderline in this._order)
    {
        stockIds.Add(orderline.StockId);
    }
    string[] array = new string[stockIds.Count];
    stockIds.CopyTo(array, 0);
    this._products = await Common.GetProducts(new ProductSearchFilter { StockId = array });

    _scanStart = Common.Now;
    Items.Clear();

    bool already_scanned = false; // Todo trouver un meilleur non car pas tres explicite

    foreach (PickingOrderLineToHandle orderLine in _order)
    {
        if (orderLine.ScannedQuantity < orderLine.Quantity)
        {
            if ((bool)orderLine.WasSentToEnd && !already_scanned)
            {
                Items.Add(new ListItem() { Name = " ----- ", Id = "-1" });
                already_scanned = true;
            }

            Product product = null;
            foreach (var prod in _products)
            {
                if (prod.StockId == orderLine.StockId)
                {
                    product = prod;
                    break;
                }
            }
            Items.Add(new ListItem() { Name = $"{(orderLine.Quantity - orderLine.ScannedQuantity).ToString()} / {orderLine.Quantity}\t + {orderLine.Table_bin.LabelAddress} {product.ProductName} {orderLine.stock.Platform}", Id = orderLine.StockId });
        }
    }

    IsBusy = false;
}

下面是页面中的调用:

protected override void OnAppearing()
{
    base.OnAppearing();

    viewModel.LoadItemsCommand.Execute(null);

    int loop = 0;
    while (viewModel.IsBusy && loop < 60)
    {
        System.Threading.Thread.Sleep(500);
        loop++;
    };

    if (loop == 60)
    {
        DisplayAlert("Erreur", "Une erreur est survenue lors du chargment. Veuillez réessayer.", "Ok");
        Navigation.PopAsync();
    }

    var cantCancel = viewModel.Items.Any(i => i.BookedQuantity > 0);

    if (Common.IsTeamLeader)
        cantCancel = false;

    if (cantCancel)
    {
        var cancelButton = this.ToolbarItems.Where(b => b.Text == "Annuler").First();
        this.ToolbarItems.Remove(cancelButton);
    }
}
thigvfpy

thigvfpy1#

问题

下面的语句看起来像是一个阻塞调用,但实际上,由于Command调用的方法是一个异步匿名函数,它将以*触发并忘记*的方式执行,并且不会等待:

viewModel.LoadItemsCommand.Execute(null);

这是因为命令是如何定义的:

LoadItemsCommand = new Command(async () => await ExecuteLoadItemsCommand());

这样的Command总是同步执行的,但是,当从同步上下文调用async void方法时,它只会开始执行,而不会执行await方法(因此 * 触发并忘记 *)。

溶液

现在,你使用Command的方式是不寻常的。通常,你会从UI控件绑定到Command,或者你只是触发它的执行。你希望它在***之前完成***其余的代码执行,这不是我上面解释的情况。
您可以执行以下操作,而不是像这样执行命令:
1.确保ExecuteLoadItemsCommand()正在返回Task
1.将ExecuteLoadItemsCommand()的可见性更改为public
1.将名称更改为ExecuteLoadItemsAsync(),这是返回异步任务的方法的常用表示法
您的新方法签名应该如下所示:

public async Task ExecuteLoadItemsAsync()
{
    //...
}

然后,在页面的代码隐藏中,您可以将async方法重写更改为异步运行,方法是将async关键字添加到签名中,然后等待ExecuteLoadItemsAsync()方法:

protected override async void OnAppearing()
{
    base.OnAppearing();

    await viewModel.ExecuteLoadItemsAsync();

    //...
}

这样,您就可以直接执行并等待该方法,Command仍然可以用于绑定任何UI元素,例如按钮。

可选改进

在ViewModel中,可以使用AsyncRelayCommand代替Command,如下所示:

private AsyncRelayCommand _loadItemsCommand;
public AsyncRelayCommand LoadItemsCommand => _loadItemsCommand ??= new AsyncRelayCommand(ExecuteLoadItemsAsync);

然而,这并不能改变这样一个事实,即您不应该在页面代码中以您一直尝试的方式执行Command。

相关问题