使用异步方法的结果
Using the result of an async method
我有一个带有签名的简单异步方法:
public async Task<bool> AllowAccessAsync(string authenticatedUserToken)
调用此方法时,将其结果分配给局部变量时我似乎有两种选择:
bool response = null;
// option 1
await this.transportService.AllowAccessAsync(authenticatedUserToken).ContinueWith(task => response = task.Result);
// option 2
response = await this.transportService.AllowAccessAsync(authenticatedUserToken);
第一种使用continuation delegate赋值给局部变量,第二种将结果直接赋值给变量。
这些结果一样吗?这两种方法都有什么优势吗?有更好的方法吗?
通过将 async/await
与任务并行库的 ContinueWith
结合使用,您会不必要地混合模式。除非您别无选择,否则有很多理由不这样做,尤其是 async/await
的 SynchronizationContext
is not preserved by the default implementation of ContinueWith.
所以简而言之,选项 2 是正确的。
Do these have the same result?
编辑:
@Servy 正确指出,因为 ContinueWith
只是结果的投影。这意味着这两个操作在语义上是等价的,但在例外情况下它们的行为会有所不同。
Are there any advantages to either approach? Is there a better way to do this?
编辑 2:
以下评论通常与 async-await
和 ContinueWith
的使用有关。具体查看这两个示例,因为它们都使用 async-await
,肯定使用后者,因为它们都包含状态机生成,但后者将传播 AggregateException
以防发生异常。
async-await
具有生成 状态机 的最小开销,而 ContinueWith
则没有。另一方面,使用 async-await
可以让你 "feel" 同步而实际上是异步的,并且可以避免 ContinueWith
的冗长。我肯定会选择async-await
,不过我建议你研究一下正确的使用方法,因为可能会有意想不到的可怜。
它与编码风格有更多关系。
当您有一个大型工作流,可能希望在执行承诺时分配不同的延续时,第一种样式可能很有用。但是它依赖于闭包。我不推荐这种用法,因为它并不总是可以预测的。 ContinueWith
应该在您创建工作流时使用,并且每个步骤仅依赖于之前的步骤,并且不与外部范围通信,除非通过交付产生最终结果的任务(您随后将等待)。
当您只对结果感兴趣时,第二个很有用。
另外 ContinueWith
允许您指定 TaskScheduler
因为您的应用程序默认附带的可能不是您想要的。
有关 TaskScheduler
的更多信息,请点击此处:
http://blog.stephencleary.com/2015/01/a-tour-of-task-part-7-continuations.html
Do these have the same result?
是的,这两个选项最终会将相同的结果设置为response
。唯一的区别发生在出现异常时。在第一个选项中,抛出的异常将是对实际异常的 AggregateException
包装,而在第二个选项中,它将是实际的异常。
Are there any advantages to either approach?
这样使用ContinueWith
绝对没有优势。第二种选择的优点是异常处理更好,读写更简单。
Is there a better way to do this?
不是,这就是你使用 async-await 的方式。
正如 Servy 评论的那样,这不是 ContinueWith
的使用方式。 ContinueWith
等效于将方法的其余部分放入延续中。所以不是这个:
public async Task FooAsync()
{
var response = await this.transportService.AllowAccessAsync(authenticatedUserToken);
Console.WriteLine(response);
}
你会这样做:
public Task FooAsync()
{
return this.transportService.AllowAccessAsync(authenticatedUserToken).
ContinueWith(task => Console.WriteLine(task.GetAwaiter().GetResult()));
}
这确实有一些性能优势,因为它不需要异步等待状态机,但要做到这一点非常复杂。
我有一个带有签名的简单异步方法:
public async Task<bool> AllowAccessAsync(string authenticatedUserToken)
调用此方法时,将其结果分配给局部变量时我似乎有两种选择:
bool response = null;
// option 1
await this.transportService.AllowAccessAsync(authenticatedUserToken).ContinueWith(task => response = task.Result);
// option 2
response = await this.transportService.AllowAccessAsync(authenticatedUserToken);
第一种使用continuation delegate赋值给局部变量,第二种将结果直接赋值给变量。
这些结果一样吗?这两种方法都有什么优势吗?有更好的方法吗?
通过将 async/await
与任务并行库的 ContinueWith
结合使用,您会不必要地混合模式。除非您别无选择,否则有很多理由不这样做,尤其是 async/await
的 SynchronizationContext
is not preserved by the default implementation of ContinueWith.
所以简而言之,选项 2 是正确的。
Do these have the same result?
编辑:
@Servy 正确指出,因为 ContinueWith
只是结果的投影。这意味着这两个操作在语义上是等价的,但在例外情况下它们的行为会有所不同。
Are there any advantages to either approach? Is there a better way to do this?
编辑 2:
以下评论通常与 async-await
和 ContinueWith
的使用有关。具体查看这两个示例,因为它们都使用 async-await
,肯定使用后者,因为它们都包含状态机生成,但后者将传播 AggregateException
以防发生异常。
async-await
具有生成 状态机 的最小开销,而 ContinueWith
则没有。另一方面,使用 async-await
可以让你 "feel" 同步而实际上是异步的,并且可以避免 ContinueWith
的冗长。我肯定会选择async-await
,不过我建议你研究一下正确的使用方法,因为可能会有意想不到的可怜。
它与编码风格有更多关系。
当您有一个大型工作流,可能希望在执行承诺时分配不同的延续时,第一种样式可能很有用。但是它依赖于闭包。我不推荐这种用法,因为它并不总是可以预测的。 ContinueWith
应该在您创建工作流时使用,并且每个步骤仅依赖于之前的步骤,并且不与外部范围通信,除非通过交付产生最终结果的任务(您随后将等待)。
当您只对结果感兴趣时,第二个很有用。
另外 ContinueWith
允许您指定 TaskScheduler
因为您的应用程序默认附带的可能不是您想要的。
有关 TaskScheduler
的更多信息,请点击此处:
http://blog.stephencleary.com/2015/01/a-tour-of-task-part-7-continuations.html
Do these have the same result?
是的,这两个选项最终会将相同的结果设置为response
。唯一的区别发生在出现异常时。在第一个选项中,抛出的异常将是对实际异常的 AggregateException
包装,而在第二个选项中,它将是实际的异常。
Are there any advantages to either approach?
这样使用ContinueWith
绝对没有优势。第二种选择的优点是异常处理更好,读写更简单。
Is there a better way to do this?
不是,这就是你使用 async-await 的方式。
正如 Servy 评论的那样,这不是 ContinueWith
的使用方式。 ContinueWith
等效于将方法的其余部分放入延续中。所以不是这个:
public async Task FooAsync()
{
var response = await this.transportService.AllowAccessAsync(authenticatedUserToken);
Console.WriteLine(response);
}
你会这样做:
public Task FooAsync()
{
return this.transportService.AllowAccessAsync(authenticatedUserToken).
ContinueWith(task => Console.WriteLine(task.GetAwaiter().GetResult()));
}
这确实有一些性能优势,因为它不需要异步等待状态机,但要做到这一点非常复杂。