我构建了一个TS,MongoDB Client包装器。出于某种原因,当我调用获取连接的函数时,其回调被调用了两次。
对get()函数总共进行了2次调用,如您所见,在导出之前进行了1次调用,而在mocha测试中进行了另一次调用。
总体来说,我对TS和JS还是很陌生,但这似乎有点不合时宜。
import {Db, MongoClient} from "mongodb"; import {MongoConfig} from '../config/config' class DbClient { private cachedDb : Db = null; private async connectToDatabase() { console.log('=> connect to database'); let connectionString : string = "mongodb://" + MongoConfig.host + ":" + MongoConfig.port; return MongoClient.connect(connectionString) .then(db => { console.log('=> connected to database'); this.cachedDb = db.db(MongoConfig.database); return this.cachedDb; }); } public async get() { if (this.cachedDb) { console.log('=> using cached database instance'); return Promise.resolve(this.cachedDb); }else{ return this.connectToDatabase(); } } } let client = new DbClient(); client.get(); export = client;
控制台输出为:
=> connect to database => connected to database => connected to database
是否有任何特殊的原因?
答案 0 :(得分:2)
对get()函数总共进行了2次调用,如您所见,在导出之前进行了1次调用,而在mocha测试中进行了另一次调用。
我怀疑输出还有另外的=> connect to database
。正如我在评论中所说:有一个“竞赛条件”,其中get()
在设置this.cachedDb
之前可以被多次调用,这将导致创建多个Db的连接/实例。
例如:
const a = client.get();
const b = client.get();
// then
a.then(resultA => {
b.then(resultB => {
console.log(resultA !== resultB); // true
});
});
解决方案
可以通过将promise作为缓存的值存储来解决此问题(而且,正如Randy所指出的那样,无需在方法上使用async
关键字,因为任何方法都没有等待值,因此您可以只兑现承诺):
import {Db, MongoClient} from "mongodb";
import {MongoConfig} from '../config/config'
class DbClient {
private cachedGet: Promise<Db> | undefined;
private connectToDatabase() {
console.log('=> connect to database');
const connectionString = `mongodb://${MongoConfig.host}:${MongoConfig.port}`;
return MongoClient.connect(connectionString);
}
get() {
if (!this.cachedGet) {
this.cachedGet = this.connectToDatabase();
// clear the cached promise on failure so that if a caller
// calls this again, it will try to reconnect
this.cachedGet.catch(() => {
this.cachedGet = undefined;
});
}
return this.cachedGet;
}
}
let client = new DbClient();
client.get();
export = client;
注意:我不确定使用MongoDB的最佳方法(我从未使用过),但是我怀疑连接的生存期不应如此长(或者应该仅为短时间然后断开连接)。不过,您需要对此进行调查。