在HttpClientRequest future上设置超时回调会抛出未处理的异常



我试图在HttpClientRequest未来的timeout方法上设置onTimeout回调。

下面的示例编译没有错误。但是,当运行时,它会抛出以下未处理的异常:

type '() => Future<HttpClientRequest>' is not a subtype of type '(() => FutureOr<_HttpClientRequest>)?' of 'onTimeout'.

我是dart/flutter的新手,很难理解回调方法失败的原因。

void main() async {
requestify();
}
Future<HttpClientRequest> requestify() async {
var client = HttpClient();
var request = client.get('10.255.255.1', 80, '');
// request.timeout(const Duration(seconds: 1));
request.timeout(const Duration(seconds: 1), onTimeout: () {
print('timed out');
return request;
});
return request;
}

让我们更仔细地看看这个类型错误:

type '() => Future<HttpClientRequest>' is not a subtype of type '(() => FutureOr<_HttpClientRequest>)?' of 'onTimeout'.

提供一个返回Future<HttpClientRequest>的回调函数。

期望的是一个返回FutureOr<_HttpClientRequest>的函数。

FutureOr<T>TFuture<T>类型的特殊并集,因此返回Future<HttpCientRequest>的函数是返回FutureOr<HttpClientRequest>的函数的子类型(即兼容)。然而,我们期望的是FutureOr<_HttpClientRequest>_HttpClientRequest吗?这是从哪里来的,为什么不兼容?

即使不知道实现细节,我们也可以假设_HttpClientRequestHttpClientRequest的某个子类型。错误消息暗示,当我们调用HttpClient.get时,返回对象的静态(在编译时已知)类型是Future<HttpClientRequest>,但实际的运行时类型是Future<_HttpClientRequest>。如果类型DerivedBase的子类型,那么Dart也将GenericClass<Derived>视为GenericClass<Base>的子类型。通常这是可以的,并且返回函数声明返回的更窄的子类型是安全的。

当您尝试调用返回的Future<_HttpClientRequest>上的.timeout时,会出现问题。Future<T>.timeout的回调函数必须返回FutureOr<T>。但是,您在运行时类型为Future<_HttpClientRequest>的对象上调用.timeout,因此您的回调也必须返回_HttpClientRequest。返回基类类型(HttpClientRequest)是无效的,因为您不能在需要更窄类型的情况下返回更宽的类型。

(参见Dart给出的未处理异常:type不是type of 'value'的子类型,在另一种情况下,将GenericClass<Derived>视为GenericClass<Base>的子类型可能导致意外的运行时错误。)

TL;博士

就分析器和编译器所知,您在Future<HttpClientRequest>上调用.timeout,并使用返回HttpClientRequest的回调,因此没有编译时错误。然而,在运行时,你实际上是在一个Future<_HttpClientRequest>上调用.timeout,并返回一个HttpClientRequest,所以你最终会得到一个运行时错误。

如何解决这个问题?

一些选择:

  • 考虑提交关于HttpClient.get返回运行时类型为Future<_HttpClientRequest>而不是Future<HttpClientRequest>的对象的Dart SDK问题。
  • 由于_HttpClientRequest是私有类型,您实际上不能自己返回该类型的对象。但是,您可以使用Future.valueFuture<_HttpClientRequest>中构造一个新的Future<HttpClientRequest>:
    // `client.get` might return a `Future` that completes to a subtype of
    // `HttpClientRequest`.
    var request = Future<HttpClientRequest>.value(client.get('10.255.255.1', 80, ''));
    request.timeout(const Duration(seconds: 1), onTimeout: () {
    print('timed out');
    return request;
    });
    return request;
    }
    
  • 通过不使用Future.timeout回调来避免回调类型的怪异,而是捕获结果TimeoutException

我还将指出Future.timeout返回一个新的Future,但是您返回的是原始的Future,并且timeout回调返回的值将永远不会被使用。我不知道这是否是你想要的,尽管在这种情况下,如果你想做的只是记录一个请求花了太长时间,这可能是好的。

Future<HttpClientRequest?> requestify() async {
try {
var client = HttpClient();
var request = await client.get("10.255.255.1", 80, "")
.timeout(const Duration(seconds: 1));
return request;
} on TimeoutException catch(e) {
return null;
}
}

then in main函数

final response = await requestify();
if (response != null) {
// HttpClientRequest
} else {
// timeout
}

相关内容

  • 没有找到相关文章