从其他异步函数中调用非异步函数时,普遍接受的最佳实践是什么?



似乎在Blazor服务器应用程序中,我们被鼓励尽可能使用Async,我通常理解为什么。也就是说-请有人解释使用Async函数时的一般期望,并为我填写一些缺失的知识-我对所有的信息感到困惑,需要一些特定于我正在做的事情,以帮助我理解。

我试着在可能的地方坚持使用异步,在大多数情况下,这是相当容易做到的,但是当调用一些函数时,使它们异步似乎是多余的(或者是吗?)在下面的例子中,非异步的'ValidateEvent'函数是从异步函数中调用的。

所以我的问题是……我应该:a)通常从Async函数内调用它(这似乎打败了Async的点)例如:"var validationResult = ValidateEvent(objDto);"?

b)将其命名为using Task。运行例如:"await Task.Run(() =>ValidateEvent(objDto));"?

c)转换这个简单的IF/ELSE方法到异步函数?

事先感谢您的帮助/建议。

//example async function, that itself calls the other non-async function.
public async Task<Response> AddAsync(ObjDto objDto)
{
// a) Call it normally?
var validationResult = ValidateEvent(objDto);
// b) Calling it using Task.Run?
var validationResult = await Task.Run(() =>ValidateEvent(objDto));

//Do stuff asynchronously here
...
await _db.AddAsync(objDto);
await _db.SaveChangesAsync();
...
}

验证函数:

c)我真的应该将此转换为异步,因为它只是一系列的' if和else '(异步转换进一步在下面)

//Non-Async version
public ResponseObj ValidateEvent(ObjDto obj)
{
ResponseObj responseObj = new();
string stringErrors = "";
//If not full day, check that end date is not before start date
if (!obj.IsFullDay)
{
if (obj.EndTime < obj.StartTime)
stringErrors += ("End date cannot be before the start date<br />");
}
...other code removed for brevity
if (string.IsNullOrEmpty(stringErrors)) //Success
{
responseObj.Status = responseObj.Success;
}
else //errors
{
responseObj.Status = responseObj.Error;
responseObj.Message = stringErrors;
}
return responseObj;
}

//示例异步转换-是否值得使用Task.Run转换?

//Async conversion
public async Task<ResponseObj> ValidateEvent(ObjDto obj)
{
ResponseObj responseObj = new();
string stringErrors = "";

await Task.Run(() =>
{
//If not full day, check that end date is not before start date
if (!obj.IsFullDay)
{
if (obj.EndTime < obj.StartTime)
stringErrors += ("End date cannot be before the start date<br />");
}
if (string.IsNullOrEmpty(stringErrors)) //Success
{
responseObj.Status = responseObj.Success;
}
else //errors
{
responseObj.Status = responseObj.Error;
responseObj.Message = stringErrors;
}
}
);
return responseObj;
}

再次,提前感谢任何帮助/建议在理解最好的方式去做这个一般。

a)从Async函数中正常调用…

是的

…(这似乎破坏了async的意义)

不,它没有。

这取决于ValidateEvent方法的作用。

  1. 如果它做了一个微不足道的工作量,不会随着时间的推移而增加,就像你的例子所建议的那样,那么从异步AddAsync方法内部调用它是完全可以的。

  2. 如果它阻塞了相当长的时间,比如超过50毫秒,那么从异步方法内部调用它将违反微软关于异步方法预期行为的指导方针:

基于TAP的异步方法可以在返回结果任务之前同步执行少量工作,例如验证参数和初始化异步操作。同步工作应该保持在最低限度,以便异步方法可以快速返回。

那么,如果ValidateEvent方法阻塞,你应该怎么做,你不能做任何事情?我敢说你正处在一个灰色地带,因为这样或那样的原因,所有的选择都不令人满意。

  1. 你可以让它保持原样,接受这个方法违反了微软的指导方针,记录这个行为,并让调用者选择在Task.Run中包装AddAsync,如果这对他们有益的话。

  2. 您可以将ValidateEvent包装在Task.Run中,就像您在问题中所做的那样。这(部分地)违反了微软的另一条指导原则:不要为同步方法公开异步包装器。

  3. 你可以把你的AddAsync方法分成两个部分,同步的ValidateAdd和异步的AddAsync,期望调用者会在另一个之后调用一个方法。

如果您的AddAsync方法是与应用程序无关的库的一部分,我会说使用选项1。这就是微软正在做的一些内置api。如果AddAsync方法是特定于应用程序的,并且应用程序有GUI (WinForms, WPF等),那么选择选项2是很有诱惑力的。对于GUI应用程序来说,保持UI响应是至关重要的,因此Task.Run应该包含在某个地方,并且将其放在AddAsync中似乎是一个方便的位置。对于这种特殊情况,选项3可能是最不吸引人的选项,因为验证和添加是紧耦合的操作,但对于其他场景,它可能是一个很好的选项。

最新更新