我试图在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>
是T
和Future<T>
类型的特殊并集,因此返回Future<HttpCientRequest>
的函数是返回FutureOr<HttpClientRequest>
的函数的子类型(即兼容)。然而,我们期望的是FutureOr<_HttpClientRequest>
。_HttpClientRequest
吗?这是从哪里来的,为什么不兼容?
即使不知道实现细节,我们也可以假设_HttpClientRequest
是HttpClientRequest
的某个子类型。错误消息暗示,当我们调用HttpClient.get
时,返回对象的静态(在编译时已知)类型是Future<HttpClientRequest>
,但实际的运行时类型是Future<_HttpClientRequest>
。如果类型Derived
是Base
的子类型,那么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.value
从Future<_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
}