只有同步内容的异步函数的最佳实践?
Best practice for async functions with only synchronous contents?
假设我有一个虚拟成员函数异步调用:
class Command {
public abstract Task<object> Execute(); // Meant to be async for implementations
}
class Caller {
Command command;
public async Task Call() {
await command.Execute(); // Command.Execute is always called with `await`
}
}
对于大多数情况,实现只是一个普通的异步逻辑:
class NormalCase : Command {
public override async Task<object> Execute() {
await ...; // Some async works
return ...;
}
}
但确实存在一些派生实现只是为了同步完成的情况——即不需要 await
。
class SyncCase : Command {
public override async Task<object> Execute() => 1; // Just a synchronous value
}
这当然有效,但会出现编译警告(CS1998)。
放置 #pragma
选项可以抑制警告,但在许多单独的文件中都有实现,我必须在所有地方放置 #pragma
s。 (非常感谢,Unity!)
所以让我们把这个解决方案放在一边。
我还尝试了其他几种可能的解决方案,但其中 none 效果很好:
class Solution1 {
public override async Task<object> Execute() {
await Task.Delay(1); // Just NASTY
return 1;
}
}
class Solution2 {
public override Task<object> Execute() => 1; // Will cause upstream calls to break
}
class Solution3 {
public override Task<object> Execute() =>
new Task<object>(() => 1); // Same, it breaks upstream logic
}
鉴于这种情况,最佳做法是什么?
答案是——不要。你在向函数的消费者撒谎。想象一下,调用一个您认为是异步的方法在后台执行某些操作,却发现您的 UI 冻结了 10 秒。
当且仅当结果已经可用时,使用 Task.FromResult
class StaticCase : Command {
public override Task<object> Execute() => Task.FromResult((object)1);
}
否则通过使用 Task.Run() 到 运行 单独线程中的阻塞代码使方法异步:
class BlockingCase : Command {
public override async Task<object> Execute()
{
var result=await Task.Run(()=>SomeHeavyWork());
return result;
}
}
假设我有一个虚拟成员函数异步调用:
class Command {
public abstract Task<object> Execute(); // Meant to be async for implementations
}
class Caller {
Command command;
public async Task Call() {
await command.Execute(); // Command.Execute is always called with `await`
}
}
对于大多数情况,实现只是一个普通的异步逻辑:
class NormalCase : Command {
public override async Task<object> Execute() {
await ...; // Some async works
return ...;
}
}
但确实存在一些派生实现只是为了同步完成的情况——即不需要 await
。
class SyncCase : Command {
public override async Task<object> Execute() => 1; // Just a synchronous value
}
这当然有效,但会出现编译警告(CS1998)。
放置 #pragma
选项可以抑制警告,但在许多单独的文件中都有实现,我必须在所有地方放置 #pragma
s。 (非常感谢,Unity!)
所以让我们把这个解决方案放在一边。
我还尝试了其他几种可能的解决方案,但其中 none 效果很好:
class Solution1 {
public override async Task<object> Execute() {
await Task.Delay(1); // Just NASTY
return 1;
}
}
class Solution2 {
public override Task<object> Execute() => 1; // Will cause upstream calls to break
}
class Solution3 {
public override Task<object> Execute() =>
new Task<object>(() => 1); // Same, it breaks upstream logic
}
鉴于这种情况,最佳做法是什么?
答案是——不要。你在向函数的消费者撒谎。想象一下,调用一个您认为是异步的方法在后台执行某些操作,却发现您的 UI 冻结了 10 秒。
当且仅当结果已经可用时,使用 Task.FromResult
class StaticCase : Command {
public override Task<object> Execute() => Task.FromResult((object)1);
}
否则通过使用 Task.Run() 到 运行 单独线程中的阻塞代码使方法异步:
class BlockingCase : Command {
public override async Task<object> Execute()
{
var result=await Task.Run(()=>SomeHeavyWork());
return result;
}
}