return Task.FromException 的正确用法

Correct usage of return Task.FromException

我最近观察到两个开发人员之间的代码审查。

提交了以下代码:

 public async Task<List<Thing>> GetThings()
    {
        try
        {
            var endpoint = $"{Settings.ThingEndpoint}/things";
            var response = await HttpClient.GetAsync(endpoint);
            return JsonConvert.DeserializeObject<List<Thing>>(await response.Content.ReadAsStringAsync());
        }
        catch (Exception e)
        {
            Log.Logger.Error(e.ToString());
            return await Task.FromException<List<Thing>>(e);
        }
    }

其中收到了如下评论意见:

There is absolutely no need to return await Task.FromException>(e), this is something you do when dealing with non awaited task. In this case the catch will capture whatever exception var response = await HttpClient.GetAsync(endpoint); will throw. You should just remove it and catch the exception as is

我不完全理解为什么在这种情况下不使用 Task.FromException,所以我有以下问题:

  1. 评论者在说什么?
  2. 审稿人是否正确?
  3. 为什么不 return 等待 Task.FromException?
  4. return await Task.FromException 的正确方案是什么?

审稿人完全正确。

您唯一会使用 Task.FromException 的情况是当您处于无法或不会使用 asyncawait 实现的方法中,并且您想要的结果是该任务应该是一个例外。

愚蠢的例子,但无论如何:

public Task<int> NotReallyAsync()
{
    if (new Random().Next(2) == 0)
        return Task.FromResult(42);

    return Task.FromException<int>(new InvalidOperationException());
}

那么让我们一一解答您的问题:

  1. 评论者说 Task.FromException 应该只用于非 async/await 方法,在 async/await 方法,您应该重新抛出异常:

    catch (Exception e)
    {
        Log.Logger.Error(e.ToString());
        throw;
    }
    

    或者如果您实施异常过滤器:

    catch (Exception e) when (Log.Logger.ExceptionFilter(e)) { }
    
  2. 是的,审稿人是正确的。

  3. 因为没必要,不如重新抛出异常。如果你想抛出异常,就抛出它。 async/await的目的是为了能够正常的写出你的方法,所以写一个正常的throw语句或者正常的catch-block.
  4. async/await 方法,仅此。
  1. 一般来说,return从 catch 中调用并不是好的编码习惯。
  2. Task.FromException 通常在您希望在满足已知失败条件时依赖任务状态时使用。例如,如果一个对象是空的,你知道你应该 return 一个错误的 task.Client 可以使用错误的任务状态来显示适当的消息 user.I 修改代码只是为了告诉你例子.

         public async Task<List<Thing>> GetThings()
        {
            try
            {
                var endpoint = $"{Settings.ThingEndpoint}/things";
                var response = await HttpClient.GetAsync(endpoint);
                var obj = JsonConvert.DeserializeObject<List<Thing>>(await response.Content.ReadAsStringAsync());
                if(obj==null)
                {
                  return await Task.FromException<List<Thing>>(new NullRefernceException());
                }
                else
                {     
    
                }
    
            }
            catch (Exception e)
            {
                Log.Logger.Error(e.ToString());
                throw;
    
            }
        }