如何在Dart中创建一个空的Future?如何返回当前正在进行的Future?

12

我试图创建一个服务器端的Dart类,执行各种数据相关的任务。所有这些任务都依赖于数据库首先已经被初始化。问题是数据库的初始化是异步的(返回一个Future)。我最初尝试将初始化代码放入构造函数中,但是放弃了这种方法,因为似乎不可行。

我现在正在尝试找出如何强制执行DB初始化作为访问数据的任何方法调用的第一步。因此,换句话说,在调用attemptLogin()时,我想首先检查DB是否已经初始化并在必要时进行初始化。

然而,有两个障碍。如果尚未初始化数据库,则代码很简单-初始化db,然后使用返回的future的then()方法来完成函数的其余部分。如果尚未初始化db,我该将then()方法附加到什么上?

第二个相关问题是当数据库正在初始化,但该过程尚未完成时会发生什么?我如何拉取并返回这个“正在进行中”的Future?

这是我试图整理的基本代码的梗概:

class DataManager {
  bool DbIsReady = false;
  bool InitializingDb = false;
  Db _db;

  Future InitMongoDB() {
    print("Initializing MongoDB");
    InitializingDb = true;
    _db = new Db("mongodb://127.0.0.1/test");
    return _db.open().then((_) {
      DbIsReady = true;
      InitializingDb = false;
    });
  }  

  Future<List> attemptLogin(String username, String password) {
    Future firstStep;
    if ((!DbIsReady) && (!InitializingDb) {
       Future firstStep = InitMongoDB()
    }
    else if (InitializingDb) {
       // Need to return the InitMongoDB() Future that's currently running, but how?
    }
    else {
       // How do I create a blank firstStep here? 
    }

    return firstStep.then((_) {
      users = _db.collection("users");
      return // ... rest of code cut out for clarity
    });
  }
}

谢谢您提前的帮助。

Greg


我觉得在任何与数据库有关的代码运行之前确保数据库初始化会更容易,而不是在每个数据库操作之前。你这样做的原因是什么? - Günter Zöchbauer
@Günter,我最初尝试在类构造函数中执行DB初始化,但在测试过程中遇到了问题。我在StackOverflow上发布了一个关于这个问题的问题:http://stackoverflow.com/questions/24592070/waiting-for-my-class-to-initialize-or-how-to-wait-for-a-future-to-complete ,但没有得到解决问题的好答案。 - Greg Sherman
我在你的另一个问题中添加了一个答案。虽然我还没有测试过它,但至少它应该能帮助你理解思路。 - Günter Zöchbauer
6个回答

18

只需返回

return new Future<bool>.value(true); 
// or any other value instead of `true` you want to return.
// or none
// return new Future.value();

4

对于那些仍然想知道如何在Dart中创建一个空的Future并稍后完成它们的人,您应该像下面的例子中使用Completer类。

class AsyncOperation {
  final Completer _completer = new Completer();

  Future<T> doOperation() {
    _startOperation();
    return _completer.future; // Send future object back to client.   
  }

  // Something calls this when the value is ready.   
  void finishOperation(T result) {
    _completer.complete(result);   
  }

  // If something goes wrong, call this.   
  void _errorHappened(error) {
      _completer.completeError(error);   
  }
}

3

只需要保持未来活着:

class DataManager {
  Future _initializedDb;

  Future initMongoDb() { ... }

  Future<List> attemptLogin(String username, String password) {
    if (_initializedDb == null) {
       _initializedDb = initMongoDB();
    }
    return _initializedDb.then((db) {
      users = db.collection("users");
      return // ... rest of code cut out for clarity
    });
  }
}

你可能需要注意错误情况。如果你想处理initMongoDB之前或之后的错误,那就由你决定。


你是说可以链到一个已完成的 future,还是在执行中的 future?我在任何例子中都没有看到过这个,并且也没有相关的文档。 - Greg Sherman
作为未来的用户,您永远不知道该值是否已经存在。所以这是正常行为。但是,当出现错误时,未来必须至少有一个监听器。否则,该错误将被视为未处理,并可能导致程序崩溃。这意味着您应尽快添加至少一个错误处理程序。 - Florian Loitsch

2

可能的解决方案之一:

import "dart:async";

void main() {
  var dm = new DataManager();
  var selectOne = dm.execute("SELECT 1");
  var selectUsers = dm.execute("SELECT * FROM users");
  var users = selectOne.then((result) {
    print(result);
    return selectUsers.then((result) {
      print(result);
    });
  });

  users.then((result) {
    print("Goodbye");
  });
}

class Event {
  List<Function> _actions = new List<Function>();
  bool _raised = false;

  void add(Function action) {
    if (_raised) {
      action();
    } else {
      _actions.add(action);
    }
  }

  void raise() {
    _raised = true;
    _notify();
  }

  void _notify() {
    if (_actions.isEmpty) {
      return;
    }

    var actions = _actions.toList();
    _actions.clear();
    for (var action in actions) {
      action();
    }
  }
}

class DataManager {
  static const int _STATE_NOT_INITIALIZED = 1;
  static const int _STATE_INITIALIZING = 2;
  static const int _STATE_READY = 3;

  Event _initEvent = new Event();
  int _state = _STATE_NOT_INITIALIZED;

  Future _init() {
    if (_state == _STATE_NOT_INITIALIZED) {
      _state = _STATE_INITIALIZING;
      print("Initializing...");
      return new Future(() {
        print("Initialized");
        _state = _STATE_READY;
        _initEvent.raise();
      });
    } else if (_state == _STATE_INITIALIZING) {
      print("Waiting until initialized");
      var completer = new Completer();
      _initEvent.add(() => completer.complete());
      return completer.future;
    }

    return new Future.value();
  }

  Future execute(String query, [Map arguments]) {
    return _init().then((result) {
      return _execute(query, arguments);
    });
  }

  Future _execute(String query, Map arguments) {
    return new Future.value("query: $query");
  }
}

输出:

Initializing...
Waiting until initialized
Initialized
query: SELECT 1
query: SELECT * FROM users
Goodbye

我认为存在更好的解决方案,但这只是尝试回答您的问题(如果我正确理解了您的问题)。

P.S. 2014年7月11日编辑

稍微修改了示例(包括错误处理)。

import "dart:async";

void main() {
  var dm = new DataManager();
  var selectOne = dm.execute("SELECT 1");
  var selectUsers = dm.execute("SELECT * FROM users");
  var users = selectOne.then((result) {
    print(result);
    return selectUsers.then((result) {
      print(result);
    });
  });

  users.then((result) {
    print("Goodbye");
  });
}

class DataManager {
  static const int _STATE_NOT_INITIALIZED = 1;
  static const int _STATE_INITIALIZING = 2;
  static const int _STATE_READY = 3;
  static const int _STATE_FAILURE = 4;

  Completer _initEvent = new Completer();
  int _state = _STATE_NOT_INITIALIZED;

  Future _ensureInitialized() {
    switch (_state) {
      case _STATE_NOT_INITIALIZED:
        _state = _STATE_INITIALIZING;
        print("Initializing...");
        new Future(() {
          print("Initialized");
          _state = _STATE_READY;
          // throw null;
          _initEvent.complete();
        }).catchError((e, s) {
          print("Failure");
          _initEvent.completeError(e, s);
        });

        break;    
      case _STATE_INITIALIZING:
        print("Waiting until initialized");
        break;
      case _STATE_FAILURE:
        print("Failure detected");
        break;
      default:
        print("Aleady intialized");
        break;
    }

    return _initEvent.future;
  }

  Future execute(String query, [Map arguments]) {
    return _ensureInitialized().then((result) {
      return _execute(query, arguments);
    });
  }

  Future _execute(String query, Map arguments) {
    return new Future.value("query: $query");
  }
}

你认为针对这个问题有更好的解决方案吗? - Greg Sherman

1

Dart中的Future<Type>是非空的,这意味着你必须将其初始化为一个值。如果没有初始化,Dart会抛出以下错误:

错误:字段应该被初始化,因为它的类型 'Future<Type>' 不允许为空。

要初始化Future<Type>,请参考以下示例:

Future<String> myFutureString = Future(() => "Future String");

"Future String"是一个字符串,因此上面的代码返回一个Future<String>实例。

那么接下来的问题是如何创建一个空的Future,我使用以下代码初始化了一个空的Future列表。

Future<List> myFutureList = Future(() => []);

我发现这个链接对于理解Flutter和Dart中的Futures非常有用:https://meysam-mahfouzi.medium.com/understanding-future-in-dart-3c3eea5a22fb


0
创建一个类型为void的黑色未来,例如:
onRefresh: () async {},

在这里,一个类型为Future<void>的空函数被分配给了onRefresh属性。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接