javascript回调在一次不可能的调用中被调用两次
我构建了一个TS、MongoDB客户机包装器。出于某种原因,当我调用获取连接的函数时,它的回调函数会被调用两次 总共有两个get()函数调用,一个在导出之前调用,另一个在mocha测试中调用 总的来说,我对TS和JS还很陌生,但这似乎有点不对劲 其中,控制台输出为: 有没有什么特别的原因导致这种行为不端 总共有两个get()函数调用,一个在导出之前调用,另一个在mocha测试中调用 我怀疑输出有一个额外的连接到数据库的javascript回调在一次不可能的调用中被调用两次,javascript,typescript,mocha.js,Javascript,Typescript,Mocha.js,我构建了一个TS、MongoDB客户机包装器。出于某种原因,当我调用获取连接的函数时,它的回调函数会被调用两次 总共有两个get()函数调用,一个在导出之前调用,另一个在mocha测试中调用 总的来说,我对TS和JS还很陌生,但这似乎有点不对劲 其中,控制台输出为: 有没有什么特别的原因导致这种行为不端 总共有两个get()函数调用,一个在导出之前调用,另一个在mocha测试中调用 我怀疑输出有一个额外的连接到数据库的=>。正如我在评论中所说:存在一个“竞争条件”,其中get()可以在之前被多次
=>。正如我在评论中所说:存在一个“竞争条件”,其中get()
可以在之前被多次调用。设置cachedDb
将导致创建多个Db连接/实例
例如:
const a = client.get();
const b = client.get();
// then
a.then(resultA => {
b.then(resultB => {
console.log(resultA !== resultB); // true
});
});
解决方案
这个问题可以通过将承诺存储为缓存值来解决(同样,正如Randy指出的,方法上不需要使用async
关键字,因为在任何方法中都没有等待的值,所以您可以只返回承诺):
从“mongodb”导入{Db,MongoClient};
从“../config/config”导入{MongoConfig}
类DbClient{
私人纪念印:承诺|未定义;
私有连接数据库(){
log('=>connecttodatabase');
const connectionString=`mongodb://${MongoConfig.host}:${MongoConfig.port}`;
返回MongoClient.connect(connectionString);
}
得到(){
如果(!this.cachedGet){
this.cachedGet=this.connectToDatabase();
//在失败时清除缓存的承诺,以便
//再次调用此,它将尝试重新连接
this.cachedGet.catch(()=>{
this.cachedGet=未定义;
});
}
返回此.cachedGet;
}
}
让client=newdbclient();
client.get();
导出=客户端;
注意:我不确定使用MongoDB的最佳方式(我从未使用过它),但我怀疑连接不应该像这样被缓存(或者应该只缓存一段时间,然后断开连接)。不过你需要调查一下
总共有两个get()函数调用,一个在导出之前调用,另一个在mocha测试中调用
我怀疑输出有一个额外的连接到数据库的=>。正如我在评论中所说:存在一个“竞争条件”,其中get()
可以在之前被多次调用。设置cachedDb
将导致创建多个Db连接/实例
例如:
const a = client.get();
const b = client.get();
// then
a.then(resultA => {
b.then(resultB => {
console.log(resultA !== resultB); // true
});
});
解决方案
这个问题可以通过将承诺存储为缓存值来解决(同样,正如Randy指出的,方法上不需要使用async
关键字,因为在任何方法中都没有等待的值,所以您可以只返回承诺):
从“mongodb”导入{Db,MongoClient};
从“../config/config”导入{MongoConfig}
类DbClient{
私人纪念印:承诺|未定义;
私有连接数据库(){
log('=>connecttodatabase');
const connectionString=`mongodb://${MongoConfig.host}:${MongoConfig.port}`;
返回MongoClient.connect(connectionString);
}
得到(){
如果(!this.cachedGet){
this.cachedGet=this.connectToDatabase();
//在失败时清除缓存的承诺,以便
//再次调用此,它将尝试重新连接
this.cachedGet.catch(()=>{
this.cachedGet=未定义;
});
}
返回此.cachedGet;
}
}
让client=newdbclient();
client.get();
导出=客户端;
注意:我不确定使用MongoDB的最佳方式(我从未使用过它),但我怀疑连接不应该像这样被缓存(或者应该只缓存一段时间,然后断开连接)。不过,您需要对此进行调查。旁注:存在一个竞争条件,在此条件下,get
可以在之前被多次调用。设置cachedDb
将导致创建多个连接/实例Db
。这可以通过将connectToDatabase
的承诺分配给私有属性,然后在get
中返回该承诺来避免(不过它可能会以某种方式处理失败)。您的承诺逻辑有缺陷。除了David的建议之外,get()
方法还返回封装在承诺中的承诺或封装在承诺中的已解析承诺(this.cachedDb
)。但是不要使用任何一个。您从MongoClient.connect()
返回承诺,但从不使用它(然后从get
返回)。最后,在promise上使用then并返回一个从未使用过的值。也许你应该回顾一下@RandyCasburn中非常简单的设计,我看不到该代码中有任何包含在承诺(承诺返回承诺)中的承诺。也许我遗漏了什么?@David-connectToDatabase()
是异步的,并返回一个Promise对象,该Promise对象由get()
直接返回,还有一个异步函数,它返回一个封装在承诺中的承诺。@Gleeb-检查这一点,这是以更简洁的方式编写的类。旁注:有一个竞争条件,在这个条件下,get
可以在之前被多次调用。cachedDb
被设置,这将导致Db
的多个连接/实例被激活创建。这可以通过将connectToDatabase
的承诺分配给私有属性,然后在get
中返回该承诺来避免(不过它可能会以某种方式处理失败)。您的承诺逻辑有缺陷。除了什么,大卫
const a = client.get();
const b = client.get();
// then
a.then(resultA => {
b.then(resultB => {
console.log(resultA !== resultB); // true
});
});
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;