如何在不更改异步方法的情况下在方法中使用 await 关键字

How to use await keyword inside a method without changing the method async

我正在开发一个计划作业以使用 Quartz.net 将消息发送到消息队列。 IJob 的 Execute 方法不是异步的。所以我不能使用异步任务。但是我想用 await 关键字调用一个方法。

请在下面找到我的代码。不确定我做的是否正确。谁能帮我解决这个问题?

private async Task PublishToQueue(ChangeDetected changeDetected)
{
    _logProvider.Info("Publish to Queue started");

    try
    {
       await _busControl.Publish(changeDetected);

        _logProvider.Info($"ChangeDetected message published to RabbitMq. Message");
    }
    catch (Exception ex)
    {
        _logProvider.Error("Error publishing message to queue: ", ex);

        throw;
    }
}

public class ChangedNotificatonJob : IJob
{
    public void Execute(IJobExecutionContext context)
    {
                    //Publish message to queue
                    Policy
                        .Handle<Exception>()
                        .RetryAsync(3, (exception, count) =>
                        {
                            //Do something for each retry
                        })
                        .ExecuteAsync(async () =>
                        {
                            await PublishToQueue(message);
                        });
    }
}

这是正确的方法吗?我用过 .GetAwaiter();

Policy
        .Handle<Exception>()
        .RetryAsync(_configReader.RetryLimit, (exception, count) =>
        {
            //Do something for each retry
        })
        .ExecuteAsync(async () =>
        {
            await PublishToQueue(message);
        }).GetAwaiter()

波莉 .ExecuteAsync() returns 一个 Task。对于任何 Task,您只需对其调用 .Wait()(或其他阻塞方法)以同步阻塞,直到它完成或抛出异常。

如您所见,由于 IJob.Execute(...) 不是 async,您不能使用 await,因此您别无选择,只能在任务上同步阻塞,如果您想在 IJob.Execute(...) returns.

之前发现发布的成功与否

.Wait() 将导致任务中的任何异常被重新抛出,包裹在 AggregateException 中。如果所有 Polly 编排的重试都失败,就会发生这种情况。

您需要决定如何处理该异常:

  • 如果你想让调用者处理它,重新抛出它或者不捕获它,让它级联到 Quartz 作业之外。

  • 如果你想在从IJob.Execute(...)返回之前处理它,你需要一个try {} catch {}围绕整个.ExecuteAsync(...).Wait()。或者考虑 Polly 的 .ExecuteAndCaptureAsync(...) 语法:它通过将执行的最终结果放入 PolicyResult 实例中来避免您必须提供外部 try-catch。参见Polly doco


如果您的唯一目的是在消息发布失败的地方记录日志,并且您不关心该日志记录是否发生在 IJob.Execute(...) returns 之前,则还有另一种选择。在这种情况下,您可以使用 .ContinueWith(...) 将延续任务链接到 ExecuteAsync(),并在其中处理任何日志记录,而不是使用 .Wait()。我们采用这种方法,并将失败的消息发布捕获到一个特殊的 'message hospital' - 捕获足够的信息,以便我们可以选择是否在适当的时候再次重新发布该消息。这种方法是否有价值取决于永不丢失消息对您的重要性。


编辑:GetAwaiter() 无关紧要。它不会神奇地让您开始在非 async 方法中使用 await