Выполнять фьючерсы до тех пор, пока параметр не станет истинным

Я запускаю запрос на сервер с будущим «requestServer». Я хотел бы опросить систему для определенного значения (перешло от ложного к истинному, когда запрос выполнен) и вернуться, когда закончите.

Код может быть таким, но «пока» синхронно, а «checkOperation» асинхронно?

return requestServer().then((operation) {
  var done = false;
  while (done)
    return checkOperation(operation).then((result) {
      done = (result == true);
    });
    sleep(10);
  }
});

Любые идеи ?


person sestegra    schedule 11.03.2014    source источник


Ответы (4)


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

void main(List<String> args) {

  // polling
  new Timer.periodic(new Duration(microseconds: 100), (t) {
    if(isDone) {
      t.cancel();
      someCallback();
    }
  });

  // set isDone to true sometimes in the future
  new Future.delayed(new Duration(seconds: 10), () => isDone = true);
}

bool isDone = false;

void someCallback() {
  print('isDone: $isDone');
  // continue processing
}

Конечно, вы можете передать обратный вызов в качестве параметра, а не жестко запрограммировать его, потому что функции являются членами первого класса в Dart.

person Günter Zöchbauer    schedule 11.03.2014

Опрос не очень хорошо работает для асинхронности. Лучше дождаться сигнала от дела, которое должно завершиться.

Ответ Гюнтера Цохбауэра показывает, как в любом случае проводить опрос, путем выборки с помощью таймера.

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

return requestServer().then((operation) {
  var completer = new Completer();
  void poll(result) {     
    if (!result) { 
      operation.then(poll, onError: completer.completeError);
    } else {
      completer.complete();
    }
  }
  poll(false);
  return completer.future;
});

(Код на самом деле не тестировался, так как у меня нет вашего requestServer).

person lrn    schedule 12.03.2014
comment
Спасибо, оба подхода имеют свои плюсы и минусы. Я подумаю, что лучше для моих нужд. - person sestegra; 12.03.2014

Когда вы хотите построить функции, которые возвращают фьючерсы, иногда полезно использовать комплекторы. Думайте, что requestServer() тоже живет в Будущем, так что вы получите результат как Будущее.

    return requestServer().then((operation) {

      // This is necessary then you want to control async 
      // funcions.
      Completer completer = new Completer();

      //
      new Timer.periodic(const Duration(seconds: 10), (_) {
        checkOperation(operation).then((result) {

          // Only when the result is true, you pass the signal
          // that the operation has finished.
          // You can alse use `completer.complete(result)` if you want
          // to pass data inside of the future.
          if (result == true) completer.complete();
        });
      });

      // You return the future straight away.
      // It will be returned by requestServer();
      return completer.future;
    });
person Daniel    schedule 13.03.2014
comment
Ваше первое предложение не совсем верно (это для вашего примера), но вы можете просто сделать return new Future(() => computation);, чтобы функция возвращала будущее без явного использования завершителя. - person Günter Zöchbauer; 13.03.2014

Я использую такую ​​функцию в TestUtil библиотеке:

  static Future<bool> waitUntilTrue(bool Function() callback,
      {Duration timeout: const Duration(seconds: 2),
      Duration pollInterval: const Duration(milliseconds: 50)}) {
    var completer = new Completer<bool>();

    var started = DateTime.now();

    poll() {
      var now = DateTime.now();
      if (now.difference(started) >= timeout) {
        completer.completeError(Exception('timed out in waitUntilTrue'));
        return;
      }
      if (callback()) {
        completer.complete(true);
      } else {
        new Timer(Duration(milliseconds: 100), () {
          poll();
        });
      }
    }

    poll();
    return completer.future;
  }

И затем в моем тестовом коде я сделаю что-то вроде:

await TestUtil.waitUntilTrue(() => someObj.isDone);

Редактировать:

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

  await tester.runAsync<bool>(
      () => TestUtil.waitUntilTrue(() => myObj.isLoaded),
      additionalTime: Duration(seconds: 5));
person user1978019    schedule 08.02.2021