You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js async未resolve、Mongo游标forEach执行后程序挂起问题

问题产生原因
  • done提前打印:run()的调用链写法错误,.then()要求传入一个函数作为回调,等Promise resolve后执行,但你写的是.then(console.log('done')),这行代码会在JS引擎执行到这一行时立刻同步调用console.log('done'),再把它的返回值undefined作为参数传给.then(),完全没有等待run()内部逻辑执行,所以done会比业务逻辑的输出更早出现。
  • 逻辑执行顺序混乱、await失效:你用的MongoDB驱动自带的cursor.forEach方法,本身不支持等待传入的async回调返回的Promise。你写的await cursor.forEach(...)实际上只是等待forEach本身同步执行完遍历触发的动作,完全不会等待每个doc回调里的异步操作(查商品库、发支付请求、更新支付状态)执行完成,相当于这些异步操作都是"悬空"的,外层run()的Promise状态不会等它们完成就变更。控制台输出2次test,是因为forEach遍历到2条文档时,会同步执行回调里的第一行console.log('test'),但后面的await逻辑会被丢到事件队列里,没有被等待。
  • 进程持续挂起不退出:所有业务逻辑跑完后,你没有主动调用client.close()关闭MongoDB客户端连接,连接池会一直保持存活,Node.js的事件循环不会清空,自然不会自动终止进程。另外回调里的异步操作如果抛出异常,因为没有被外层await监听,会变成未捕获的Promise异常,也可能导致进程卡住。
修复方案
  • 修正Promise链的回调写法:把done的打印逻辑包在函数里传给.then(),确保run()所有逻辑执行完成后再触发;所有异常都能被外层的catch正常捕获。
  • 替换不支持async等待的cursor.forEach,两种常用写法可选,都能保证每一条文档的异步处理逻辑按顺序执行、被外层await等待:
    • 数据量不大时,用await cursor.toArray()把所有查询到的文档转成数组,再用普通for...of循环遍历处理,写法简单直观。
    • 数据量较大不想全量加载到内存时,直接用for await...of异步迭代cursor,逐条拉取文档处理,内存占用更低。
  • 所有业务逻辑执行完成后,主动调用client.close()关闭MongoDB连接,让进程正常退出。

修正后的核心参考代码:

async function successfullyPaid(doc){
    return await updateMongoDBStuff();
}

async function run() {
    // 异步方法必须加await等待执行完成
    await findMongoDBstuff();

    // 写法1:全量拉取文档转数组后遍历,适合数据量小的场景
    // const docs = await cursor.toArray();
    // for (const doc of docs) {

    // 写法2:直接异步迭代游标,适合大数据量场景,内存占用更低
    for await (const doc of cursor) {
        console.log('test')
        if (doc.plan.nextPayment <= getUnixTimeSeconds()) {
            if (parseInt(doc.plan.remaining) >= 0){
                const allProds = client.db('ProductsDB').collection('allProds')
                const prod = await allProds.findOne({})
                const amount = prod.recurring.periods[doc.plan.period]

                const data = {
                    "transaction": {
                        "amount" : "1000",
                        "payment_method_token": `visa_card`,
                        "currency_code": "USD",
                        "retain_on_success": true,
                        "email" : doc.client.email
                    }
                }
                const response = await superagent.post(`https://example.com`)
                    .send(data)
                    .set('accept', 'json')
                    .auth('xxxxxxxxxxxxxxxxxxxxxxxxx', 'xxxxxxxxxxxxxxxxxxxxxxxxxx');

                if (response.body.transaction.succeeded == true){
                    await successfullyPaid(doc)
                } else {
                    console.log(response)
                }
            }
        }
    }
}

// 修正then/catch回调写法,执行完成后主动关闭数据库连接
run()
    .catch((error)=>{
        console.log(error);
    })
    .then(async ()=>{
        console.log('done')
        await client.close()
    })

内容的提问来源于stack exchange,提问作者Vibeman1987

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 03:42:17