обратный вызов JavaScript вызывается дважды при невозможном вызове - PullRequest
0 голосов
/ 10 июня 2019

Я создал оболочку 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

Есть какая-то конкретная причина, по которой он плохо себя ведет?

1 Ответ

2 голосов
/ 10 июня 2019

Всего есть 2 вызова функции get (), 1 перед экспортом, как вы можете видеть, и еще один из теста мокко.

Я подозреваю, что выход имеет дополнительный=> 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
    });
});

Решение

Проблема может быть решена путем сохранения обещания в качестве кэшированного значения (также нет необходимости указывать ключевое слово 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 (яЯ никогда не использовал его), но я подозреваю, что соединения не должны быть такими долгоживущими, чтобы их можно было так кэшировать (или, вероятно, их следует кэшировать только на короткое время, а затем отключить).Вы должны будете исследовать это все же.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...