我正在开发一个WPF XBAP应用程序,它通过使用BrowserInteropHelper通过JavaScript为用户提供API。在以新的异步等待方式重写托管部分之后,需要等到异步操作完成而不使调用方法异步任务本身。我有:
public string SyncMethod(object parameter)
{
var sender = CurrentPage as MainView; // interop
var result = string.Empty;
if (sender != null)
sender.Dispatcher.Invoke(DispatcherPriority.Normal,
new Action(async () =>
{
try
{
result = await sender.AsyncMethod(parameter.ToString());
}
catch (Exception exception)
{
result = exception.Message;
}
}));
return result;
}
此方法返回string.Empty,因为这在执行后立即发生。我试图将此操作分配给DispatcherOperation并执行while(dispatcherOperation.Status!=已完成){Wait(); ,但即使在执行后,该值仍为空。 dispatcherOperation.Task.Wait()或dispatcherOperation.Task.ContinueWith()也没有帮助。
的 修改
也尝试了
var op = sender.Dispatcher.BeginInvoke(...);
op.Completed += (s, e) => { var t = result; };
但是t也是空的,因为赋值在等待异步方法之前发生。
编辑
长话短说,SyncMethod JavaScript互操作处理程序帮助程序包装器,它运行的所有操作必须在Dispatcher上。 AsyncMethod是异步任务< T>并且在内部应用程序中执行和等待时工作完美,即在某个按钮处理程序中。它有很多依赖于UI的逻辑(显示状态进度条,更改光标等)
的 修改
AsyncMethod:
public async Task<string> AsyncMethod(string input)
{
UIHelpers.SetBusyState(); // overriding mouse cursor via Application.Current.Dispatcher
ProgressText = string.Empty; // viewmodel property reflected in ui
var tasksInputData = ParseInput(input);
var tasksList = new List<Task>();
foreach (var taskInputDataObject in tasksInputData)
{
var currentTask = Task.Factory.StartNew(() =>
{
using (var a = new AutoBusyHandler("Running operation" + taskInputData.OperationName))
{ // setting busy property true/false via Application.Current.Dispatcher
var databaseOperationResult = DAL.SomeLongRunningMethod(taskInputDataObject);
ProgressText += databaseOperationResult;
}
});
tasksList.Add(insertCurrentRecordTask);
}
var allTasksFinished = Task.Factory.ContinueWhenAll(tasksList.ToArray(), (list) =>
{
return ProgressText;
});
return await allTasksFinished;
}
答案 0 :(得分:2)
在以新的async-await方式重写托管部分之后,需要等到异步操作完成而不使调用方法异步任务本身。
这是你可以尝试做的最困难的事情之一。
AsyncMethod是异步任务&lt; T>并且在内部应用程序中执行和等待时工作完美,即在某个按钮处理程序中。它有很多与UI相关的逻辑
这种情况几乎不可能。
问题是您需要阻止在UI线程上运行的SyncMethod
,同时允许AsyncMethod
分派到UI消息队列。
您可以采取以下几种方法:
Task
或Wait
阻止Result
。您可以在ConfigureAwait(false)
中使用AsyncMethod
来避开deadlock problem described on my blog。这意味着AsyncMethod
将 重构为使用IProgress<T>
或其他非阻塞回调替换与UI相关的逻辑。就个人而言,我认为(1)更清洁。
答案 1 :(得分:0)
这样做:
var are = new AutoResetEvent(true);
sender.Dispatcher.Invoke(DispatcherPriority.Normal,
new Action(async () =>
{
try { ... } catch { ... } finally { are.Set(); }
}));
are.WaitOne();
return result;
are.WaitOne()将告诉AutoResetEvent等待它发出信号,这将在调度程序操作到达finally块时发生,其中调用is.Set()。
答案 2 :(得分:-1)
这为我解决了。
Add Task delay for invoker to complete。
public string SyncMethod(object parameter)
{
var sender = CurrentPage as MainView; // interop
var result = string.Empty;
if (sender != null)
{
await sender.Dispatcher.Invoke(DispatcherPriority.Normal,
new Func<Task>(async () =>
{
try
{
result = await sender.AsyncMethod(parameter.ToString());
}
catch (Exception exception)
{
result = exception.Message;
}
}));
}
await Task.Delay(200); // <-- Here
return result;
}