未来的 catchError 函数在未立即链接时不会捕获错误

Future catchError function doesn't catch error when its not chained immediately

考虑这段代码:
示例 A:

Future<void> fakeCall() async {
  await Future.delayed(const Duration(milliseconds: 300), () {
    throw MyError('myException');
  });
}

Future<void> fetch() async {
  Future<void> testFuture = fakeCall();

  testFuture.whenComplete(() {
    print('when completed');
  });
  testFuture.catchError((e) {
    print('on error');
  });
  return testFuture;
}

void main() async {
 try{
  await fetch();
  }on MyError catch(e){
     //getting here but one still leaks!!
  }
}

当调用 await fetch() 时,我希望只调用 catchError。但是错误以某种方式泄漏,所以基本上有两个被抛出...一个被 catchError 捕获,另一个作为未处理的错误泄漏到外面。

但使用建议的方式(一个接一个地链接):
示例 B:

Future<void> fakeCall() async {
  await Future.delayed(const Duration(milliseconds: 300), () {
    throw MyError('myException');
  });
}

Future<void> fetch() async {
  Future<void> testFuture = fakeCall().whenComplete(() {
    print('when completed');
  }).catchError((e) {
    print('on error');
  });

  return testFuture;
}

void main() async {
 try{
  await fetch();
  }on MyError catch(e){
     //getting here and nothing leaks!!
  }
}

没有发生错误泄漏。 这让我很好奇, 谁能解释一下为什么第一种写法会导致错误泄露?


更新

原来我忘了在fetch函数周围加上try/catch,所以 您可能认为错误是由于这个原因而泄露的。但请注意,当没有正确链接 onError 时,错误仍然会泄漏(第一个示例)

错误已泄露,因为您正在等待 Future 的结果,最终完成时出现以下错误:

await fetch();

重要的是要了解 Future 对象只是一个值,它有时会在将来获得一个值,这个值可以是一个值也可以是一个错误。

catchError() 是一种订阅一些代码的方法,如果 Future 作为错误完成,则要执行的代码。方法 returns 一个新的未来,其逻辑是它在原始未来完成的情况下获得原始值而没有任何错误。但是,如果 origin 完成时出现错误,catchError() 将运行指定的方法并 returns 执行后的值。

catchError() 不会改变原点 Future 的任何内容,因此您可以根据需要多次调用 catchError()

我应该补充一点,我认为在异步标记的函数中使用传统的 try...catch(结合 await)比使用 catchError() 更好,因为它使得代码更易读,更不易混淆:

Future<void> fetch() async {
  try {
    await fakeCall();
  } catch (e) {
    print('on error');
  } finally {
    print('when completed');
  }
}

void main() async {
  await fetch();
}

输出:

on error
when completed

问题更改后更新

“示例 B”的问题在于您假设 whenComplete() 确实处理了任何错误。但是如果你阅读文档:

The future returned by this call, f, will complete the same way as this future unless an error occurs in the action call, or in a Future returned by the action call. If the call to action does not return a future, its return value is ignored.

https://api.dart.dev/stable/2.15.1/dart-async/Future/whenComplete.html

所以返回的 Future 没有改变它的行为,所以如果原点 Future 完成时出现错误,从 whenComplete() 创建的 Future 也会发生同样的情况.由于您没有在此 Future 上附加任何错误处理,您的程序失败了。