8

I'm trying to create a server-side Dart class that performs various data-related tasks. All of these tasks rely on the database having been first initialized. The problem is that the init of the database happens asynchronously (returns a Future). I first tried to put the init code into the constructor, but have given up on this approach as it seems to not be viable.

I am now attempting to figure out how to force the DB initialization as a first step in any method call that accesses data. So in other words, when attemptLogin() is called below, I'd like to first check if the DB has been initialized and initialize it if necessary.

However, there are two obstacles. If the database hasn't been initialized, the code is straightforward - initialize the db, then use the then() method of the returned future to do the rest of the function. If the db is not yet initialized, what do I attach my then() method to?

Second related question is what happens when a database is currently being initialized but this process is not yet complete? How can I pull in and return this "in-progress" Future?

This is the basic gist of the code I'm trying to wrangle:

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
    });
  }
}

Thanks in advance for your help,

Greg

4

5 回答 5

12

刚回来

return new Future<bool>.value(true); 
// or any other value instead of `true` you want to return.
// or none
// return new Future.value();
于 2014-07-10T14:06:40.223 回答
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它或之后的错误,这取决于你。

于 2014-07-10T21:54:18.727 回答
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

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

PS 于 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");
  }
}
于 2014-07-10T15:18:15.483 回答
0

对于那些仍然想知道如何Future在 Dart 中创建空白并稍后完成它们的人,您应该使用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);   
  }
}
于 2021-10-12T16:44:28.040 回答
0

Future<Type> 在 Dart 中不可为空,这意味着您必须将其初始化为一个值。如果不这样做,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

于 2022-01-19T22:00:22.470 回答