首页 > 解决方案 > 无法捕获异步异常

问题描述

这是我的问题的一个简单示例:

import 'dart:async';

void main() {
  try {
    Timer(Duration(seconds: 1), () {
      throw TimeoutException('1 second has expired');
    });
  } catch (o) {
    print("caught ${o.runtimeType}");
  }
}

我想问题是定时器在 try-catch 块终止后完成倒计时,考虑到倒计时是异步的并且初始化Timer成功。如何在不更改回调函数的情况下捕获异常Timer

在我的具体情况下,我正在使用flutter_blue并且在使用 async 方法时遇到了问题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;
}

我这样调用方法:

try {
  await (device as BluetoothDevice).connect(timeout: Duration(seconds: 1));
} catch (o) {
  print("caught ${o.runtimeType}");
}

考虑到我等待BluetoothDevice#connect()withawait并且计时器在与 with 成功连接(在方法结束时)时被取消timer?.cancel();,我不知道为什么 try-catch 没有捕获以下内容TimeoutException

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): 

我看到堆栈跟踪从 开始BluetoothDevice#connect(),但我不知道该怎么做。

我也尝试调用返回的 bythen((_) {}, (o) => print("caught ${o.runtimeType}"))然后在 try-catch 中等待它,但我没有成功。Future<void>BluetoothDevice#connect()

有任何想法吗?

标签: flutterdartasync-awaittry-catchtimeoutexception

解决方案


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}");
}

推荐阅读