Issue
I am currently building a Stomp WebSocket connection for Web where i want to control the connection timeout value. This is my connect method:
Future<WebSocketChannel> connect(StompConfig config) {
final completer = Completer<HtmlWebSocketChannel>();
final webSocket = WebSocket(config.url)..binaryType = BinaryType.list.value;
var onOpenEvent = webSocket.onOpen.first;
if (config.connectionTimeout.inMilliseconds > 0) {
onOpenEvent = onOpenEvent.timeout(config.connectionTimeout);
}
onOpenEvent.then((value) {
completer.complete(HtmlWebSocketChannel(webSocket));
});
webSocket.onError.first.then((err) {
completer.completeError(WebSocketChannelException.from(err));
});
return completer.future;
}
and this is how i am calling it:
try {
_channel = await platform.connect(config);
} catch (err) {
print('Caught error');
}
Sadly i seem to be unable to catch the TimeoutException
thrown by the onOpenEvent.timeout(..)
. Instead it just prints this (seemingly uncaught exception):
Error: TimeoutException after 0:00:05.000000: Future not completed
at Object.createErrorWithStack (http://localhost:59275/dart_sdk.js:5044:12)
at Object._rethrow (http://localhost:59275/dart_sdk.js:37476:16)
at async._AsyncCallbackEntry.new.callback (http://localhost:59275/dart_sdk.js:37472:13)
at Object._microtaskLoop (http://localhost:59275/dart_sdk.js:37332:13)
at _startMicrotaskLoop (http://localhost:59275/dart_sdk.js:37338:13)
at http://localhost:59275/dart_sdk.js:33109:9
What am i doing wrong? My previous code looked like this and worked for TimeoutException
, but sadly not for any other exception thrown by the WebSocket:
Future<WebSocketChannel> connect(StompConfig config) async {
final webSocket = WebSocket(config.url)..binaryType = BinaryType.list.value;
var onOpenEvent = webSocket.onOpen.first;
if (config.connectionTimeout.inMilliseconds > 0) {
onOpenEvent = onOpenEvent.timeout(config.connectionTimeout);
}
await onOpenEvent;
return HtmlWebSocketChannel(webSocket);
}
Thanks for any suggestions!
Solution
You can catch the exception, you just don’t.
The statement
onOpenEvent.then((value) {
completer.complete(HtmlWebSocketChannel(webSocket));
});
listens on the onOpenEvent
future, and creates a new Future
as result.
When the onOpenEvent
completes with the timeout exception, this then
invocation gets the error, and since it doesn’t handle the exception, the future it returned also completes with the same exception.
Nobody ever touches that future since the statement above discards it, and therefore that future’s result is an unhandled error.
The reason your previous code worked was that await onOpenEvent
would (re)throw that error and make it the result of the entire connect
call.
The new code does not have a way for that error to reach the connect
function call, the onOpenEvent.then(...)
future might complete after connect
has returned.
You can try adding an error handler to the then
call:
onOpenEvent.then((value) {
completer.complete(HtmlWebSocketChannel(webSocket));
}, onError: (error, StackTrace stackTrace) {
if (!completer.isCompleted) completer.completeError(error, stackTrace);
});
webSocket.onError.first.then((err) {
if (!completer.isCompleted) {
completer.completeError(WebSocketChannelException.from(err));
}
});
Answered By – lrn
Answer Checked By – Marie Seifert (FlutterFixes Admin)