JS构造函数异步连接MongoDB未完成即调用类方法问题求解
问题解决方案
问题根因
JavaScript的构造函数本身不支持异步等待,你当前在构造函数中直接调用回调式的connectDb属于非阻塞操作:实例创建返回的时机远早于MongoDB连接建立完成的时机,此时直接调用saveToken必然会出现连接未就绪的错误。
你确实需要通过Promise改造异步逻辑,但不推荐强行在构造函数内包裹Promise实现异步构造,这类写法可维护性很差,推荐按如下步骤改造:
改造步骤
1. 将回调式的connectDb改造为async/Promise写法
去掉回调参数,直接返回连接相关的实例,方便后续await调用:
const { MongoClient } = require('mongodb'); async function connectDb() { const url = 'mongodb://localhost/zoho_whatsapp'; const dbName = 'zoho_auth'; const client = new MongoClient(url, { useNewUrlParser: true }); await client.connect(); const db = client.db(dbName); const oauthTokenCollection = db.collection('oauthtokens'); return { client, db, oauthTokenCollection }; }
2. 重构CustomStore类,抽离异步初始化方法
构造函数只做基础属性声明,新增init方法专门处理数据库连接,所有依赖数据库的类方法都先等待初始化完成再执行业务逻辑:
class CustomStore extends TokenStore { constructor() { super(); // 初始化连接相关占位属性 this.dbClient = null; this.db = null; this.tokenCollection = null; this.initPromise = null; } // 异步初始化方法,自带防重复调用逻辑 async init() { // 已经在初始化/初始化完成时直接返回现有Promise,避免重复建连 if (this.initPromise) return this.initPromise; this.initPromise = (async () => { const { client, db, oauthTokenCollection } = await connectDb(); this.dbClient = client; this.db = db; this.tokenCollection = oauthTokenCollection; console.log("Connected to db"); })(); return this.initPromise; } async saveToken(user, token) { // 执行操作前先确保数据库连接就绪 await this.init(); // 此处可安全操作数据表 await this.tokenCollection.updateOne( { userId: user.id }, { $set: { token } }, { upsert: true } ); console.log("The token is saved"); } }
3. 调整调用逻辑
两种调用方式都可以避免时序问题:
- 方式一:服务启动阶段提前初始化(推荐,适合服务端常驻场景)
// 服务启动时先完成数据库连接 const tokenStore = new zohoPersists.CustomStore(); await tokenStore.init(); // 后续业务逻辑中可直接调用方法 await tokenStore.saveToken(user, token);
- 方式二:不提前初始化,调用方法时自动完成建连
saveToken内部已经自带初始化等待逻辑,即使不提前调用init,第一次执行saveToken时也会自动等待连接完成再操作,不会出现时序错误:
// 直接实例化调用即可,无需提前init await new zohoPersists.CustomStore().saveToken(user, token);
注意事项
- 如果你继承的
TokenStore基类要求saveToken必须是同步方法(部分老旧SDK有此限制),必须采用第一种提前初始化的方式,在传入SDK前就await init()确保连接完全就绪,不能在运行时边用边初始化。 - 记得在
init和saveToken方法外层加try/catch捕获连接异常、操作异常,避免未捕获错误导致进程退出。
内容的提问来源于stack exchange,提问作者Maxima
相关产品推荐
相关产品推荐

