javascript回调在不可能的调用中被调用了两次

时间:2019-06-10 13:34:48

标签: javascript typescript mocha

我构建了一个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

是否有任何特殊的原因?

1 个答案:

答案 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的最佳方法(我从未使用过),但是我怀疑连接的生存期不应如此长(或者应该仅为短时间然后断开连接)。不过,您需要对此进行调查。