TypeScript异步异常:Promise未等待checkMarketplaceStatus执行完成
异步执行顺序异常问题排查与解决
问题场景
学习TypeScript并采用函数式编程,编写了从MongoDB查询文档后批量调用SQL Server获取数据的程序。将SQL Server数据获取逻辑封装在checkMarketplaceStatus异步函数中,主逻辑通过Promise.allSettled批量调用该函数后,后续then分支中data为undefined。调试发现checkMarketplaceStatus内的await未生效,函数未执行完成就进入后续分支,但单独执行SQL数据获取逻辑可正常返回预期数据。
相关代码
sql.ts代码
const dbConfig: sql.config = { driver: "msnodesqlv8", connectionString: 'Driver={ODBC Driver 17 for SQL Server};Server=server.com;Database=dbName;Trusted_Connection=yes;', options: { trustedConnection: true, encrypt: true, trustServerCertificate: true, } } async function sqlConnect(config: sql.config):Promise<sql.ConnectionPool>{ let pool = await sql.connect(config) return pool } async function createPreparedStatement(conn:sql.ConnectionPool,query, inputParams:string[]):Promise<sql.PreparedStatement>{ const ps = await new sql.PreparedStatement(conn); ps.input('id',sql.NVarChar) await ps.prepare(query) return ps } async function executePreparedStatement(inputParams, ps: sql.PreparedStatement){ const result = await ps.execute(inputParams) return result }
checkMarketplaceStatus函数代码
async function checkMarketplaceStatus(item){ let systemAssign = item["Id"] const connection:sql.ConnectionPool = await sqlConnect(dbConfig) const ps: sql.PreparedStatement = await createPreparedStatement(connection, "select Column_Name from Table_Name where Id = @id", ['id']) const data = await executePreparedStatement({id: systemAssign},ps) if (ps){ await ps.unprepare(); } return {mongoData: item, sqlData:data.recordset[0]} }
主逻辑代码
...code to fetch document from mongo }).then((data)=>{ const promises = data.map( (item)=>{ return checkMarketplaceStatus(item) }) return Promise.allSettled(promises) }).then((data)=>{ console.log(data) //-----data is undefined, NOT EXPECTED })
问题分析
createPreparedStatement中的错误await:new sql.PreparedStatement(conn)是同步构造函数,不需要加await,错误的await会破坏Promise链的执行顺序,导致后续异步操作异常。- 未处理异步错误:
checkMarketplaceStatus函数没有错误捕获逻辑,一旦SQL操作抛出异常,会导致Promise变为rejected状态,若Promise.allSettled处理不当,可能导致后续then接收异常结果。 - 连接泄漏风险:SQL连接使用后未主动关闭,可能导致连接池耗尽,间接影响异步流程的执行。
解决方案
1. 修正createPreparedStatement的同步构造
移除new sql.PreparedStatement(conn)前的await,确保构造逻辑正常:
async function createPreparedStatement(conn:sql.ConnectionPool,query, inputParams:string[]):Promise<sql.PreparedStatement>{ const ps = new sql.PreparedStatement(conn); // 移除多余await ps.input('id',sql.NVarChar) await ps.prepare(query) return ps }
2. 为checkMarketplaceStatus添加错误捕获与连接清理
确保函数无论成功失败都能返回明确结果,同时关闭SQL连接避免泄漏:
async function checkMarketplaceStatus(item){ try { const systemAssign = item["Id"] const connection:sql.ConnectionPool = await sqlConnect(dbConfig) const ps: sql.PreparedStatement = await createPreparedStatement(connection, "select Column_Name from Table_Name where Id = @id", ['id']) const data = await executePreparedStatement({id: systemAssign},ps) if (ps) await ps.unprepare(); await connection.close(); // 主动关闭连接 return {mongoData: item, sqlData: data.recordset[0]} } catch (err) { // 捕获错误并返回明确的失败结果 return {mongoData: item, error: err.message} } }
3. 主逻辑中校验上游数据并处理结果
确认MongoDB返回的data是有效数组,同时正确处理Promise.allSettled的返回值:
// ...MongoDB查询代码 }).then((data)=>{ // 校验MongoDB返回的数据有效性 if (!Array.isArray(data) || data.length === 0) { console.warn('MongoDB未查询到有效数据'); return []; } const promises = data.map(item => checkMarketplaceStatus(item)) return Promise.allSettled(promises) }).then((results)=>{ console.log(results); // 现在会输出包含所有Promise结果的数组 // 可按需拆分成功/失败结果 const successful = results.filter(r => r.status === 'fulfilled').map(r => r.value); const failed = results.filter(r => r.status === 'rejected').map(r => r.reason); })
内容的提问来源于stack exchange,提问作者Tushar Saurabh
相关产品推荐
相关产品推荐

