javascript 回调在一次不可能的调用中被调用了两次
javascript callback is called twice in an impossible invocation
我构建了一个 TS,MongoDB 客户端包装器。由于某种原因,当我调用获取连接的函数时,它的回调被调用了两次。
总共调用了 2 次 get() 函数,如您所见,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
这是行为不端的任何特殊原因?
There are 2 calls in total to the get() function, 1 before the export as you can see and another from a mocha test.
我怀疑输出有一个额外的 => connect to database
。正如我在评论中所说:有一个 "race condition",其中 get()
可以在设置 this.cachedDb
之前多次调用,这将导致创建多个 connections/instances Db。
例如:
const a = client.get();
const b = client.get();
// then
a.then(resultA => {
b.then(resultB => {
console.log(resultA !== resultB); // true
});
});
解决方案
可以通过将 promise 存储为缓存值来解决这个问题(另外,不需要像 Randy 指出的那样在方法上使用 async
关键字,因为在任何方法,这样你就可以 return 承诺):
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 的最佳方式(我从未使用过),但我怀疑连接不应该存在太久以至于像这样缓存(或应该只缓存一小段时间然后断开连接)。不过,您需要对此进行调查。
我构建了一个 TS,MongoDB 客户端包装器。由于某种原因,当我调用获取连接的函数时,它的回调被调用了两次。
总共调用了 2 次 get() 函数,如您所见,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
这是行为不端的任何特殊原因?
There are 2 calls in total to the get() function, 1 before the export as you can see and another from a mocha test.
我怀疑输出有一个额外的 => connect to database
。正如我在评论中所说:有一个 "race condition",其中 get()
可以在设置 this.cachedDb
之前多次调用,这将导致创建多个 connections/instances Db。
例如:
const a = client.get();
const b = client.get();
// then
a.then(resultA => {
b.then(resultB => {
console.log(resultA !== resultB); // true
});
});
解决方案
可以通过将 promise 存储为缓存值来解决这个问题(另外,不需要像 Randy 指出的那样在方法上使用 async
关键字,因为在任何方法,这样你就可以 return 承诺):
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 的最佳方式(我从未使用过),但我怀疑连接不应该存在太久以至于像这样缓存(或应该只缓存一小段时间然后断开连接)。不过,您需要对此进行调查。