【发布时间】:2018-06-27 22:00:25
【问题描述】:
我正在尝试以编程方式跟踪重定向链(对于在线广告像素),但超时时间为 2 秒(换句话说,如果重定向链需要超过 2 秒才能解决,我希望它中止并返回 null)。
我的代码(或多或少)同步运行,所以我不得不做一些杂技来做我想做的事,但从功能上讲,它似乎工作......除了超时部分。
我有一些像这样的异步助手:
public static async Task<TResult> TimeoutAfter<TResult>(this Task<TResult> task, TimeSpan timeout)
{
using (var timeoutCancellationTokenSource = new CancellationTokenSource())
{
var completedTask = await Task.WhenAny(task, Task.Delay(timeout, timeoutCancellationTokenSource.Token));
if (completedTask != task)
{
throw new TimeoutException();
}
timeoutCancellationTokenSource.Cancel();
return await task;
}
}
public static T ToSynchronousResult<T>(this Task<T> task)
{
return Task.Run(async () => await task).Result;
}
TimeoutAfter() 辅助方法改编自 SO 文章,可在here 找到。在我的服务中,我有一个类似于这样的方法:
public string GetFinalUrl(string url)
{
string finalUrl;
try
{
finalUrl = FollowDestinationUrl(url).TimeoutAfter(TimeSpan.FromSeconds(2)).ToSynchronousResult();
}
catch (TimeoutException)
{
finalUrl = null;
}
return finalUrl;
}
private async Task<string> FollowDestinationUrl(string url)
{
var request = _webRequestFactory.CreateGet(url);
var payload = await request.GetResponseAsync();
return payload.ResponseUri.ToString();
}
这里的_webRequestFactory 返回一个写成IHttpRequest 的HttpWebRequest 抽象。
在我的成功案例单元测试中(响应不到 2 秒),我得到了我期望的结果:
private class TestWebResponse : WebResponse
{
public override Uri ResponseUri => new Uri("https://www.mytest.com/responseIsGood");
}
[TestMethod]
public void RedirectUriUnderTimeout()
{
//arrange
var service = GetService();
A.CallTo(() => _httpRequest.GetResponseAsync()).ReturnsLazily(() => new TestWebResponse());
A.CallTo(() => _httpRequest.GetResponseString())
.ReturnsLazily(() => VALID_REQUEST_PAYLOAD);
//act
var url = service.GetFinalUrl("https://someplace.com/testurl");
//assert
Assert.IsNotNull(url);
}
...但是,当我尝试实施延迟以验证超时是否正常工作时,它并没有像我预期的那样中止:
[TestMethod]
public void RedirectUriUnderTimeout()
{
//arrange
var service = GetService();
A.CallTo(() => _httpRequest.GetResponseAsync()).ReturnsLazily(() => {
Thread.Sleep(TimeSpan.FromSeconds(3));
return new TestWebResponse();
});
A.CallTo(() => _httpRequest.GetResponseString())
.ReturnsLazily(() => VALID_REQUEST_PAYLOAD);
//act
var url = service.GetFinalUrl("https://someplace.com/testurl");
//assert
Assert.IsNull(url);
}
它似乎等待了整整三秒钟,然后返回具有非空 ResponseUri 的 TestWebResponse。
我不知道我的实现是否存在根本问题,或者我的测试是否存在问题,但显然我正在以一种我不希望的方式阻止异步调用。
谁能帮我找出我做错了什么?
【问题讨论】:
标签: c# asynchronous async-await timeout