异步MVVM冻结GUI



我的目标是

  1. 开始GUI效果,
  2. 等待一些异步工作而不冻结GUI
  3. 做最终的GUI效果

我已经使用以下

的ViewModel准备了第一个演示代码
member this.RunSetStatus() = 
    async {
            this.Status <- "!Start resetting @" + DateTime.Now.ToString "yy.MM.dd hh:mm:ss"
            let! task = async {
                do! Async.Sleep (10 * 1000) 
                return "!Reset done @" + DateTime.Now.ToString "yy.MM.dd hh:mm:ss"
            }
            this.Status <- task
        } |> Async.StartImmediate

它的行为如预期的,所以我对上述内容感到满意。问题是当我用真正的阻止工作(例如WCF消费者)取代演示中的睡眠时,取回了一些结果。

member this.CheckReport(user : string) =
    async {
        let endpoint = new ServiceEndpoint(ContractDescription.GetContract(typeof<IClaimService>),
                        new BasicHttpBinding(),
                        new EndpointAddress(address))
        let factory = new ChannelFactory<IClaimService>(endpoint)
        let channel = factory.CreateChannel()
        let resp = channel.CheckReport(user) 
        factory.Close()
        return resp
    }

从我的最终代表命令

打电话给
let RefreshLogic() = 
    this.RefreshIsActive <- true
    async {
        let cons = ConsumerLib.ConsumerWCF() 
        let! task, msg = async {
            try
                let! resp = cons.CheckReport(Environment.UserName.ToLower()) 
                return resp , "" 
            with 
            |exc -> return [||], (ConsumerLib.FindInner(exc).Message + ConsumerLib.FindInner(exc).StackTrace)
            }
        this.Reports <- task
        this.RefreshIsActive <- false
        this.StatusMsg <- msg
        this.ExportCommand.RaiseCanExecuteChanged() 
        } |> Async.StartImmediate

不幸的是,它在刷新时冻结了GUI(为什么?)

问题是您的CheckReport函数。虽然它是一个异步块,但它实际上从未调用任何异步工作(即通过let!do!绑定什么),因此整个块都同步运行。

即使工作在异步工作流程内部,当您使用StartImmediate时,该工作也同步运行到第一个实际的异步函数调用,该功能将受let!do!绑定。由于您的工作是完全同步的,因此这会向上传播,最终是同步的,阻止了UI。

如果您的WCF绑定设置为包括任务返回的异步版本,则最好的方法是使用WCF方法的异步版本,看起来像:

let! resp = channel.CheckReportAsync(user) |> Async.AwaitTask

最新更新