等待我的班级初始化(或如何等待 Future 完成)?
Posted
技术标签:
【中文标题】等待我的班级初始化(或如何等待 Future 完成)?【英文标题】:Waiting for my class to initialize (or how to wait for a Future to complete)? 【发布时间】:2014-07-06 01:59:51 【问题描述】:Dart 中的期货是我存在的祸根。
我有一个类,它调用异步(Future)函数来启动数据库实例,如下所示:
class DataManager
bool DbIsReady = false;
Db _db;
DataManager()
init_mongo_db();
void init_mongo_db()
print("Initializing MongoDB");
_db = new Db("mongodb://127.0.0.1/test");
_db.open().then((_)
DbIsReady = true;
);
Future<List> attemptLogin(String username, String password)
users = _db.collection("users");
return // ... rest of code cut out for clarity
这在服务器端可以正常工作,因为当服务器第一次启动时,数据库会被初始化。当用户实际尝试登录时,数据库已经初始化并且一切正常。但是,当我尝试为此创建集成测试时,这会惨遭失败。当我创建类时,数据库尚未初始化,因此运行 AttemptLogin 例程失败。
DataManager db = new DataManager();
// This fails miserably, because db hasn't initialized yet.
db.AttemptLogin("test", "test");
更糟糕的是,代码使用了 Dart DI 框架,因此我实际上无法直接控制 DataManager 类的初始化。这是该类的实际设置:
setUp(()
app.addModule(new Module()
..bind(DataManager)
app.setUp();
);
然后这是测试登录功能的调用,最终调用失败的尝试登录函数:
var req = new MockRequest("/user/login", contentType: 'JSON', method:'POST',
body:JSON.encode(
"username" : 'test',
"password" : 'test' ));
如何处理数据库初始化的异步特性并仍然进行模拟测试?具体来说,有没有办法强制尝试登录()未来以某种方式等待 DataManager 类初始化的完成?
感谢您的帮助, 格雷格
【问题讨论】:
【参考方案1】:如果你的类有异步设置,你就不能使用普通的构造函数。 在这种情况下,我只使用返回未来的工厂方法:
class DataManager
final Db _db;
DataManager._(this._db);
static Future<DataManager> createNew()
var db = new Db("mongodb://127.0.0.1/test");
return db.open().then((_) => new DataManager._(db));
...
;
我不知道你是如何将它连接到你的依赖注入框架中的,但我想它可以用静态方法做一些事情。
【讨论】:
您可以使用将来的等待来使异步同步。但是,从长远来看,最好只返回一个未来。 Future.wait 仍然返回一个Future,它可以一次等待多个Future,并返回一个带有结果列表的Future。没有办法将异步结果变成同步结果,因为同步结果需要在函数返回时可用,而异步结果还没有。 我同意 lrn 对此的理解——future.wait 不会暂停程序执行。 @lrn:我在我的程序中尝试了你的答案,但没有成功。 DI 框架(原来是标准的 dart DI 框架:pub.dartlang.org/packages/di)尝试注入 _Future 对象并失败。错误消息是“无法执行 server_lib.IsUserLoggedIn - 类型 '_Future' 不是 'db' 的类型 'DataManager' 的子类型。” 看起来di 框架不处理异步创建的值。也许向作者提出要求。它显然还只是一个原型(根据github.com/angular/di.dart)。【参考方案2】:使用@lrn 的解决方案怎么样
setUp(()
return DataManager.createNew().then((dm)
app.addModule(new Module()
..bind(DataManager, toValue: dm);
app.setUp();
);
这样你已经将一个初始化的DataManager
实例传递给了DI。如果您稍后请求它,您始终可以确定它已经初始化。
【讨论】:
这看起来很合理——我今晚晚些时候要试试这个。不是 100% 确定 setUp() 函数也允许 Futures,但值得一试。 unittest 方法对期货有特定的支持。如果您返回未来,则仅在未来返回时执行下一步。在这种情况下,在createNew.then(()...) returns
之前不会启动任何测试这对于test('xxx', () return someAsync();
也是如此,其中测试运行程序在评估测试结果之前等待未来返回。提示:很容易忘记,如果你有嵌套的异步方法,每个方法都应该在调用前有一个 return 以拥有一个完全连接的链。
代码完美运行!每天学些新东西。 :) 谢谢,君特!这似乎是解决这个问题的最明智的方法,所以我将朝着这个方向前进。以上是关于等待我的班级初始化(或如何等待 Future 完成)?的主要内容,如果未能解决你的问题,请参考以下文章