Поведение Дарта runZoned () на асинхронно модифицированных телах - PullRequest
0 голосов
/ 11 октября 2019

Я не понимаю этого поведения ...

main(args) async {
  await runZoned(() {
    throw false;
  }, onError: (e) async {
    print("working in onError");
    await runZoned(() {
      throw false;
    }, onError: (e) async {
      print("error 1");
    });
    print("error 2");
  });
  print("finish");
}

, работающего в onError

ошибка 1

ошибка 2

finish

main(args) async {
  await runZoned(() async {
    throw await Future.error(false);
  }, onError: (e) async {
    print("working in onError");
    await await runZoned(() async {
      throw await Future.error(false);
    }, onError: (e) async {
      print("error 1");
    });
    print("error 2");
  });
  print("finish");
}

работает в onError

error 1

эти асинхронные в runZoned () радикально изменяют поведение, если они предполагаютсячтобы сделать это?

Мне нужны эти асинхронные и всегда достижения print("finish"). Как мне решить проблему?

Ответы [ 2 ]

2 голосов
/ 11 октября 2019

Вы попали в острый край зон фьючерсов и ошибок (-обработка).

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

Код

main(args) async {
  await runZoned(() async {
    await Future.error(false);
  }, onError: (e) async {
    print("working in onError");
    await runZoned(() async {
      await Future.error(false);
    }, onError: (e) async {
      print("error 1");
    });
    print("error 2");
  });
  print("finish");
}

приводит аргументы к runZone в новых зонах ошибок. Это асинхронные функции, возвращающие фьючерсы, поэтому await Future.error(false) сгенерирует и завершит возвращенное будущее с ошибкой в ​​этой конкретной зоне ошибки.

Затем, когда runZoned завершится, он вернет это будущее сошибка, созданная в другой зоне ошибки, чем при вызове. Это await ed (в корневой зоне, которая является другой зоной ошибок), поэтому await runZoned(() async { ... }, ...) никогда не завершает . await ожидает завершения будущего, будущее отказывается сообщать слушателю ошибку, с которой оно завершилось, поэтому ничего не происходит.

Фактически вы ожидаете будущее, которое никогда не завершается, чтоВот почему ваша программа останавливается в этой точке.

Так что это работает как задумано - ошибка никогда не покидает зону ошибки, в которой она была создана, но оставляет внешнюю зону без какого-либо результата.

Первый пример работает, потому что вы бросаете синхронно, а не возвращаете будущее. Синхронный бросок перехватывается немедленно, а затем runZoned возвращает null. (Это, однако, должно измениться, когда Дарт получит ненулевые типы).

В общем, вероятно, плохая идея возвращать фьючерсы, которые могут содержать ошибки из runZoned с помощью обработчика onError. Мы не можем предотвратить это (возвращение Object нормально, а фьючерсы являются объектами), но, возможно, это следует задокументировать.

0 голосов
/ 12 октября 2019

Благодаря ответу Irn я понял и использовал Completer для достижения моей цели:

main(args) async {
  var completer = Completer();
  await runZoned(() {
    Future.microtask((){
      throw Future.error(false);
    });
    completer.complete();
  }, onError: (e) {
    // ...
    completer.complete();
  });
  print("finish");
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...