【问题标题】:Async task within MvvmCross Command not returningMvvmCross 命令中的异步任务未返回
【发布时间】:2017-05-13 22:01:23
【问题描述】:

我正在 Xamarin PCL 项目中使用 MvvMCross 构建一个小项目,并且遇到了我在绑定到按钮的命令中调用的异步任务的问题。

我有一个虚假的 Web 服务,我只需调用 Task.Delay(3000)。当进程到达这一点时,它只是静止不动,什么也不做。

我最初使用 .wait() 调用进行命令调用,但在某处读到这是一个阻塞调用,不能与“异步/等待”相结合

有人可以帮忙吗?请给我一个提示,告诉我我在命令绑定上哪里出错了?

https://bitbucket.org/johncogan/exevaxamarinapp是公共的git repo,具体命令是

public ICommand SaveProfile

在 ProfileViewModel.cs 文件中。

具体代码为:

public ICommand SaveProfile
    {
        get
        {
            return new MvxCommand(() =>
            {
                if (_profile.IsValidData())
                {
                    // Wait for task to compelte, do UI updates here
                    // TODO Throbber / Spinner
                    EnumWebServiceResult taskResult;
                    Mvx.Resolve<IProfileWebService>().SendProfileToServer(_profile).Wait();

                    if(_profileWebService.getLastResponseResult() == true){
                        taskResult = EnumWebServiceResult.SUCCESS;
                    }else{
                        taskResult = EnumWebServiceResult.FAILED_UNKNOWN;
                    }
                    //_profileWebService.SendProfileToServer(_profile).Wait();
                    // Close(this);
                }
            });
        }
    }

web服务类()是:

using System;
using System.Threading.Tasks;
using ExevaXamarinApp.Models;

namespace ExevaXamarinApp.Services
{
public class FakeProfileWebService : IProfileWebService
{
    public int _delayPeriod { get; private set; }
    public bool? lastResult;

    /// <summary>
    /// Initializes a new instance of the <see cref="T:ExevaXamarinApp.Enumerations.FakeProfileWebService"/> class.
    /// </summary>
    /// 3 second delay to simulate a remote request
    public FakeProfileWebService()
    {
        _delayPeriod = 3000;
        lastResult = null;
    }

    private Task Sleep()
    {
        return Task.Delay(3000);
    }

    public bool? getLastResponseResult(){
        return lastResult;
    }

    /// <summary>
    /// Sends the profile to server asynchronously
    /// </summary>
    /// <returns>EnumWebServiceResultFlag value</returns>
    /// <param name="profileObject">Profile model object</param>
    public async Task SendProfileToServer(Profile profileObject)
    {
        // Validate arguments before attempting to use web serivce
        if (profileObject.IsValidData())
        {
            // TODO: Return ENUM FLAG that represents the state of the result
            await Sleep();
            lastResult = true;
        }else{
            lastResult = false;
        }
    }
}
}

【问题讨论】:

  • Mvx.Resolve&lt;IProfileWebService&gt;().SendProfileToServer(_profile).Wait();
  • 是的,这就是我调用异步函数的地方,但是一旦进入“SendProfileToServer”并到达 Sleep() 函数,它就什么也不做。删除 .wait() 调用也无济于事。有没有办法让“公共 ICommand SaveProfile”获取调用/属性异步?

标签: c# asynchronous xamarin.ios async-await mvvmcross


【解决方案1】:

请试试这个:

    public ICommand SaveProfile
    {
        get
        {
            return new MvxCommand(async () =>              // async added
            {
                if (_profile.IsValidData())
                {
                    // Wait for task to compelte, do UI updates here
                    // TODO Throbber / Spinner
                    EnumWebServiceResult taskResult;
                    await Mvx.Resolve<IProfileWebService>().SendProfileToServer(_profile).ConfigureAwait(false);         // await, confi.. added

                    if(_profileWebService.getLastResponseResult() == true){
                        taskResult = EnumWebServiceResult.SUCCESS;
                    }else{
                        taskResult = EnumWebServiceResult.FAILED_UNKNOWN;
                    }
                    //_profileWebService.SendProfileToServer(_profile).Wait();
                    // Close(this);
                }
            });
        }
    }

    private async Task Sleep()                                 // async added
    {
        return await Task.Delay(3000).ConfigureAwait(false);   // await, confi... added
    }

    public async Task SendProfileToServer(Profile profileObject)
    {
        // Validate arguments before attempting to use web serivce
        if (profileObject.IsValidData())
        {
            // TODO: Return ENUM FLAG that represents the state of the result
            await Sleep().ConfigureAwait(false);                      // await, confi... added
            lastResult = true;
        }else{
            lastResult = false;
        }
    }

问题是,来自 UI 的上下文和异步会导致死锁。

【讨论】:

  • 哇,肖恩,太棒了,谢谢!在过去的 3 个小时里,这一直困扰着我。只需要删除 Task Sleep() 中的“返回”,否则你的解决方案是 100%。
  • @JohnCogan,请注意虽然MvxCommand 构造函数采用Action 而不是Func&lt;Task&gt;,所以你提供了一个async void lambda,这可能有很多问题。如需更合适的解决方案,请查看Stephen Cleary's Async Programming : Patterns for Asynchronous MVVM Applications: Commands
  • @JohnCogan:对这种死锁有一个完整的解释on my blog
  • 这里使用MvxAsyncCommand 代替MvxCommand 可能是一个不错的选择。
猜你喜欢
  • 1970-01-01
  • 2013-04-16
  • 2015-01-12
  • 2014-10-01
  • 2016-08-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多