【问题标题】:Call async method from non-async method in blazor web assembly从 Blazor Web 程序集中的非异步方法调用异步方法
【发布时间】:2021-12-30 16:04:16
【问题描述】:

我正在尝试使用 Blazor WASM 开发一个可扩展的应用程序,该应用程序从服务器下载插件,并将它们缓存在浏览器存储中。 下载和缓存 API 都是异步的。

下面的AssemblyLoadContext,负责加载插件程序集。 由于插件可能包含多个程序集文件,并且项目应该支持延迟加载引用的程序集,我正在处理 Resolving 事件如下:

public class PluginAssemblyLoadContext : AssemblyLoadContext
{
    private readonly IPluginResolver? _pluginResolver;

    public PluginAssemblyLoadContext(string name) : this(name, null)
    {

    }

    public PluginAssemblyLoadContext(string name, IPluginResolver? pluginResolver) : base(name, isCollectible: true)
    {
        _pluginResolver = pluginResolver;

        if (pluginResolver != null)
        {
            Resolving += ResolveAssembly;
        }
    }

    protected override Assembly? Load(AssemblyName name)
    {
        return null;
    }

    private Assembly? ResolveAssembly(AssemblyLoadContext context, AssemblyName assemblyName)
    {
        try
        {
            var assembly = AssemblyLoadContext.Default.Assemblies.FirstOrDefault(x => x.GetName().Name == assemblyName.Name);

            if (assembly == null)
            {
                var task = _pluginResolver?.ResolveAsync(context, assemblyName);
                if (task != null)
                {
                    task.Wait();            // <- The problem!
                    assembly = task.Result; // <- The problem!
                }
            }

            if (assembly == null)
            {
                // TODO: Log...
            }

            return assembly;
        }
        catch (Exception ex)
        {
                // TODO: Log...
            throw;
        }
    }
}

问题是我无法从此方法调用异步 API,因为它会引发异常,提示“无法在此运行时等待监视器”。我不知道我应该如何同步调用它们。

感谢您的帮助

【问题讨论】:

    标签: c# async-await blazor-webassembly


    【解决方案1】:

    您似乎正试图通过调用.Wait() 来阻止主执行线程。这很糟糕,因为主线程可能负责处理 UI 更新。如果它被阻止 - 应用程序的 UI 将在您的任务进行时被冻结。我相信这是您收到此错误的主要原因。

    我可能提出的解决方案 - 不要等到任务完成。相反,通过.ContinueWith() 向它添加回调。在该回调中,您可以处理执行结果并完成您的业务逻辑(在您的情况下 - 执行日志记录)。

    【讨论】:

      【解决方案2】:

      是否可以将其重写为 async-await?那是唯一的异步方式,Blazor Webassambly 目前可以处理。 或者更脏的方式: 调用一个js函数,它回调

      [JSInvokable] public async Task<Assembly?> GetTaskResult(Task task)
          => await task;
      

      【讨论】:

        【解决方案3】:

        我担心 AssemblyLoadContext 并没有真正让您有任何其他选择。例如,在 ASP.NET Core 中,当您要编写中间件时,您可以这样做:

        app.Use((context, next) =>
        {
            return next();
        });
        

        在这种情况下,调用以下重载:

        现在如果您需要进行异步调用,您可以这样做:

        app.Use(async (context, next) =>
        {
            await next();
        });
        

        但这只是可能的,因为 ASP.NET Core 为您提供了 Use 的重载(其中回调参数通常为 Func&lt;Task&gt; 类型):

        所以底线是:AFAIK 这是不可能的,只要AssemblyLoadContext 不提供具有Task&lt;Assembly&gt; 返回类型的事件。 你最好的选择可能是在 Github 上创建一个问题。

        【讨论】:

          【解决方案4】:

          代替

          var task = _pluginResolver?.ResolveAsync(context, assemblyName);
          

          只要得到任务的结果

          var result = _pluginResolver?.ResolveAsync(context, assemblyName).Result;
          

          【讨论】:

          • 恐怕“.Result”调用还是会阻塞执行线程。错误本身可能会消失。但是,下划线问题仍然存在 - 主线程将被阻塞。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-03-10
          • 2017-03-06
          • 1970-01-01
          • 2016-07-20
          • 1970-01-01
          相关资源
          最近更新 更多