Cannot catch an asynchronous exception
That connect
method from the package you're using is poorly written. You're not doing anything wrong, whoever wrote that code did. If you poke around the GitHub Issues sections of that repository you'll find quite a few issues and pull requests related to this problem like this and the issues/PR it links.
The code in a timer callback exists outside of the function where the timer is instantiated. It's impossible to directly catch an error thrown in a timer callback.
If you want a timeout, don't use the functionality provided by this package, use the native Dart timeout
function.
try {
await (device as BluetoothDevice).connect().timeout(Duration(seconds: 1));
} catch (o) {
print("caught ${o.runtimeType}");
}
ReubenBeeler
Updated on November 23, 2022Comments
-
ReubenBeeler over 1 year
Here is a simple example of my problem:
import 'dart:async'; void main() { try { Timer(Duration(seconds: 1), () { throw TimeoutException('1 second has expired'); }); } catch (o) { print("caught ${o.runtimeType}"); } }
I imagine the problem is that the timer finishes counting down after the try-catch block terminates, considering that the countdown is asynchronous and initializing the
Timer
was successful. How can I catch the exception without changing the callback function of theTimer
?In my specific case, I am using flutter_blue and having trouble with the async method
BluetoothDevice#connect()
./// Establishes a connection to the Bluetooth Device. Future<void> connect({ Duration? timeout, bool autoConnect = true, }) async { var request = protos.ConnectRequest.create() ..remoteId = id.toString() ..androidAutoConnect = autoConnect; Timer? timer; if (timeout != null) { timer = Timer(timeout, () { disconnect(); throw TimeoutException('Failed to connect in time.', timeout); }); } await FlutterBlue.instance._channel .invokeMethod('connect', request.writeToBuffer()); await state.firstWhere((s) => s == BluetoothDeviceState.connected); timer?.cancel(); return; }
I called the method like so:
try { await (device as BluetoothDevice).connect(timeout: Duration(seconds: 1)); } catch (o) { print("caught ${o.runtimeType}"); }
Considering I wait for
BluetoothDevice#connect()
withawait
and timer is cancelled upon a successful connection (at the end of the method) withtimer?.cancel();
, I do not know why the try-catch is not catching the followingTimeoutException
:E/flutter ( 3710): [ERROR:flutter/lib/ui/ui_dart_state.cc(199)] Unhandled Exception: TimeoutException after 0:00:01.000000: Failed to connect in time. E/flutter ( 3710): #0 BluetoothDevice.connect.<anonymous closure> (package:flutter_blue/src/bluetooth_device.dart:33:9) E/flutter ( 3710): #1 _rootRun (dart:async/zone.dart:1346:47) E/flutter ( 3710): #2 _CustomZone.run (dart:async/zone.dart:1258:19) E/flutter ( 3710): #3 _CustomZone.runGuarded (dart:async/zone.dart:1162:7) E/flutter ( 3710): #4 _CustomZone.bindCallbackGuarded.<anonymous closure> (dart:async/zone.dart:1202:23) E/flutter ( 3710): #5 _rootRun (dart:async/zone.dart:1354:13) E/flutter ( 3710): #6 _CustomZone.run (dart:async/zone.dart:1258:19) E/flutter ( 3710): #7 _CustomZone.bindCallback.<anonymous closure> (dart:async/zone.dart:1186:23) E/flutter ( 3710): #8 Timer._createTimer.<anonymous closure> (dart:async-patch/timer_patch.dart:18:15) E/flutter ( 3710): #9 _Timer._runTimers (dart:isolate-patch/timer_impl.dart:395:19) E/flutter ( 3710): #10 _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:426:5) E/flutter ( 3710): #11 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12) E/flutter ( 3710):
I see that the stack trace starts at
BluetoothDevice#connect()
, but I am not sure what to do about it.I also tried calling
then((_) {}, (o) => print("caught ${o.runtimeType}"))
on theFuture<void>
returned byBluetoothDevice#connect()
and then waiting for it inside the try-catch, yet I had no success.Any ideas?
-
ReubenBeeler over 2 yearsThank you. I found a solution to my specific case, which is using BluetoothDevice#disconnect() because it apparently still works while asynchronously executing BluetoothDevice#connect(). Other than that, however, this answer is exactly what I was looking for.
-
Christopher Moore over 2 years@ReubenBeeler If my answer helped, please accept it by clicking the checkmark to the left of it.