Почему это будущее никогда не завершается? - PullRequest
0 голосов
/ 21 июня 2020

У меня есть следующий код:

class FileData {
  Future<String> get _localPath async {
    print('D: Local'); //D
    final directory = await getApplicationDocumentsDirectory();
    print('D: Directory: $directory'); //D
    return directory.path;
  }

  Future<File> get _localFile async {
    final path = await _localPath;
    print('D: Path: $path'); //D
    return File('$path/file.dat');
  }
  
  Future<File> _future;
  Future<RandomAccessFile> _futureRandomAccessFile;
  File _file;
  RandomAccessFile _randomAccessFile;

  FileData() {
    print('D: First'); //D
    _future = _localFile;
    _future = _future.then(
      (File file) {
        print('D: Stuff'); //D
        if(file == null) throw 'file == null';
        _file = file;
        print('D: Foo'); //D
        _futureRandomAccessFile = file.open(mode: FileMode.append);
        _futureRandomAccessFile = _futureRandomAccessFile.then(
          (RandomAccessFile randomAccessFile) {
            if(randomAccessFile == null) throw 'randomAccessFile == null';
            _randomAccessFile = randomAccessFile;

            print('D: Position: ${_randomAccessFile.positionSync()}'); //D
            int byte = DateTime.now().second; //D
            print('D: Write: $byte'); //D
            _randomAccessFile.writeByte(byte); //D

            return randomAccessFile;
          },
          onError: (e) {
            throw e;
          }
        ).timeout(Duration(seconds: 2));
        return file;
      },
      onError: (e) {
        print('D: Bad'); //D
        throw e;
      }
    ).timeout(Duration(seconds: 2));
  }

  _syncRandomAccessFile() {
    while(_randomAccessFile == null) {
      print('Waiting on _randomAccessFile...');
    }
  }

  void close() {
    _syncRandomAccessFile();

    print('D: Position: ${_randomAccessFile.positionSync()}'); //D
    _randomAccessFile.setPositionSync(0); //D
    print('D: Position: ${_randomAccessFile.positionSync()}'); //D
    print('D: Byte: ${_randomAccessFile.readByteSync()}'); //D
    int byte = DateTime.now().second; //D
    print('D: Write: $byte'); //D
    _randomAccessFile.writeByte(byte); //D

    _randomAccessFile.close();
    _randomAccessFile = null;
  }
}

FileData fileData;

И это состояние домашней страницы моего приложения:

@override
  void initState() {
    super.initState();
    fileData = FileData();
    sleep(Duration(seconds: 1));
    fileData.close();
  }

Я ожидал бы следующую последовательность событий:

D: First
D: Local
(some or none) Waiting on _randomAccessFile...
D: Directory: <whatever>
D: Path: <whatever>
D: Stuff
D: Foo
(some or none) Waiting on _randomAccessFile...
D: Position: <whatever>
D: Write: <whatever>
D: Position: <whatever>
D: Position: 0
D: Byte: <whatever>
D: Write: <whatever>

Вместо этого я получаю:

D: First
D: Local
(a lot of) Waiting on _randomAccessFile...
(app freezes)

Я не понимаю, почему так должно быть. Очевидно, мне нужно, чтобы файл был открыт перед чтением / записью / закрытием, и мне нужно, чтобы он был в переменной экземпляра, чтобы иметь возможность использовать его как в синхронном, так и в асинхронном коде. Насколько я понимаю, будущее, возвращаемое getApplicationDocumentsDirectory(), никогда не завершается по какой-то причине.

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

Ответы [ 2 ]

1 голос
/ 21 июня 2020

Ваш вызов sleep не соответствует вашим ожиданиям, поскольку он не позволяет запускать асинхронные операции.

Спящий режим на время, указанное в длительности.

Используйте это осторожно, поскольку никакие асинхронные операции не могут обрабатываться в изолированном объекте, пока он заблокирован в вызове сна.

https://api.flutter.dev/flutter/dart-io/sleep.html

Кроме того, это не очень идея иметь асинхронные операции внутри конструктора, поскольку вы не можете быть уверены, что объект полностью создан. Вместо этого создайте внутри FileData метод stati c, который может возвращать Future<FileData> и предназначен для выполнения всех асинхронных операций перед возвратом полностью построенного объекта. При этом вам не нужно sleep.

0 голосов
/ 21 июня 2020
Комментарий

julemand101 дал ответ на этот вопрос. На всякий случай, если кто-то найдет это и ищет подробное объяснение того, что я узнал из него ...

Dart однопоточный. Операции Asyn c завершаются только после завершения текущей операции и возврата управления к событию l oop. await каким-то образом гарантирует, что это произойдет. Это также произошло бы, если бы я поместил это fileData.close() в другую функцию, которая вызывалась из события l oop. Но я вызвал конструктор, а затем дождался завершения будущего, не возвращая управление промежуточному событию l oop, поэтому будущее никогда не завершалось, и оно просто заперто навсегда.

...