Как бросить будущее через waitFor в Dart - PullRequest
0 голосов
/ 27 января 2020

Я являюсь автором пакета Dart dshell.

https://pub.dev/packages/dshell

Dshell - это библиотека и инструмент для написания сценариев dart cli.

Dshell использует waitFor, чтобы скрыть фьючерсы от пользователей, поскольку они мало используются в типичном приложении cli.

Моя проблема в том, что если в будущем выдается необработанное исключение во время обработки waitFor, оно по существу закрывает приложение вниз.

Мне нужно иметь возможность захватить любое исключение, а затем позволить вызывающему абоненту решить, что делать с этим исключением.

Вот то, что я пробовал до сих пор. Ни одна из техник улова не уловит необработанное исключение:

import 'dart:async';

import 'dart:cli';

void main() {
  var future = throwException();
  try {
    future
        .catchError((Object e, StackTrace st) => print('onErrr: $e'))
        .whenComplete(() => print('future completed'));
    print(waitFor<int>(future));
  } // on AsyncError
  catch (e) {
    if (e.error is Exception) {
      print(e.error);
    } else if (e is AsyncError) {
      print('Rethrowing a non DShellException ${e}');
      rethrow;
    } else {
      print('Rethrowing a non DShellException ${e}');
      rethrow;
    }
  } finally {
    print('waitForEx finally');
  }
}

Future<int> throwException() {
  var complete = Completer<int>();

  Future.delayed(Duration(seconds: 2), () => throw Exception());
  return complete.future;
}


У дротика waitFor есть строка, заставляющая меня думать, что это может быть невозможно:

Если Будущее завершится нормально , его результат возвращается. Если Future завершается с ошибкой, ошибка и трассировка стека помещаются в AsyncError и выбрасываются. Если микропроцессор или обработчик сообщений, запущенный во время этого вызова, приводит к необработанному исключению, то это исключение будет распространяться так, как если бы микротак или обработчик сообщений был единственным вызовом Dart в стеке. То есть необработанные исключения в микрозадаче или обработчике сообщений будут пропускать стеки, приостановленные при вызове waitFor.

Так что меня немного смущает различие между «Будущее завершается с ошибкой» и «микрозадача ... приводит к необработанному исключению».

Ответы [ 2 ]

2 голосов
/ 28 января 2020

Future, возвращаемое вашим throwException, никогда не завершится ни значением, ни ошибкой. Ошибка, выдаваемая Future.delayed, является необработанной асинхронной ошибкой c, она полностью не связана с Future, возвращаемым этим методом. Способы получения Future, которое завершается с ошибкой:

  • Конструктор Future.error.
  • Использование Completer.completeError для еще не завершенного Completer.
  • Использование throw в методе async.
  • Использование throw в обратном вызове, переданном конструктору Future, или .then.

Так в вашем примере Future.delayed создает Future, который завершится с ошибкой из-за throw в обратном вызове. Ничто не слушает это будущее. Там нет await, .then или .catchError, связанных с ним. Когда Future завершается с ошибкой, и у него нет обработчиков для этой ошибки, он всплывает до окружающей зоны ошибки. См. https://dart.dev/articles/archive/zones#handling -asynchronous-errors

Если вы хотите иметь возможность реагировать на необработанные ошибки, вы можете использовать runZoned - получить правильные детали может быть сложно. Обратите внимание, что возможно возникновение нескольких необработанных асин c ошибок, возникающих в результате выполнения некоторого кода, и что завершение Future не обязательно означает, что нет других необработанных асин c ошибок, которые могут появиться позже.

0 голосов
/ 28 января 2020

От Нейта Боша я придумал возможный ответ:

Я не понял, что вы можете добавить несколько методов onCatchError в будущее. В DShell я пропустил будущее, поэтому предположил, что не смогу его изменить.

Поэтому я добавил onCatchError в Future.delayed, а затем с помощью завершителя передал ошибку обратно в правильный стек.

Так что это похоже на работу, я просто не уверен, нужно ли мне на самом деле реализовать зону, чтобы бросить мой улов net немного дальше?

import 'dart:async';

import 'dart:cli';

void main() {
  var future = throwExceptionV3();
  try {
    future
        .catchError((Object e, StackTrace st) => print('onErrr: $e'))
        .whenComplete(() => print('future completed'));
    print(waitFor<int>(future));
  } // on AsyncError
  catch (e) {
    if (e.error is Exception) {
      print(e.error);
    } else if (e is AsyncError) {
      print('Rethrowing a non DShellException ${e}');
      rethrow;
    } else {
      print('Rethrowing a non DShellException ${e}');
      rethrow;
    }
  } finally {
    print('waitForEx finally');
  }
}

Future<int> throwExceptionV3() {
  var complete = Completer<int>();
  try
  {

    var future = Future.delayed(Duration(seconds: 2), () => throw Exception());
    future.catchError((Object e) { 
       print('caught 1'); 
       complete.completeError('caught ') ;
     });
  }
  catch (e)
  {
    print ('e');

  }
  return complete.future;
}


...