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

递归Redis函数中的async/await使用问题排查

Redis-om递归遍历循环链的两个问题

我是async/await新手,现在要用Redis-om写递归函数NN_walkd遍历Redis数据库查找循环链,遇到两个问题:

  1. 我在内部递归调用NN_walkd时用的async/await写法对吗?
  2. 运行时compSearchM能正常调用(返回5条数据,需要调用5次NN_walkd),但递归调用NN_walkd后第一次循环调用compSearchK时,卡在了第一个Redis的.search调用上,而compSearchK和compSearchM代码结构基本一致。

相关代码

主调用

NN_walk = async function(req, db, cnode, pnode, chain, cb) {
  var vegas, sneaker;

  req.session.walk = [];
  await NN_walkd(req, cnode, pnode, [], 1);
  req.session.walk = null;
  console.log('~~~~~~~~~~~~ Out of Walk ~~~~~~~~~~~~~~~');
  cb();
};

redis.mjs

export class RedisDB {
  constructor() {
    ...
    this._companyRepo = ...
  }
  compSearchK(ckey) {   // doesn't matter if I have a async or not here
    return new Promise(async (resolve) => {
      const sckey = await this._companyRepo.search()
        .where('COMPANYKEY').equals(ckey)
        .return.all();
      if (sckey.length) {
        const ttrr = await this._companyRepo.fetch(sckey[0].entityId);
        resolve(ttrr.toJSON());
      } else
        resolve(null);
    });
  }

  compSearchM(mkey) {
    var tArr=[];
    return new Promise(async (resolve) => {
      const smkey = await this._companyRepo.search()
        .where('MASTERKEY').equals(mkey)
        .and('TBLNUM').equals(10)
        .return.all();
      if (smkey.length) {
        for (var spot in smkey) {
          const ttrr = await this._companyRepo.fetch(smkey[spot].entityId);
          tArr.push(ttrr.toJSON());
        }
        resolve(tArr);
      } else {
        resolve(null);
      }
    });
  }
}

walk.js

NN_walkd = async function(req, cnode, pnode, chain, lvl) {
  ...
  if (cnode[1]) {
    const sObj = await req.app.get('redis').compSearchK(cnode[1]);
    if (sObj) {
      int1 = (sObj.TBLNUM==1) ? null : sObj.CLIENTKEY;
      (async () => await NN_walkd(req, [sObj.COMPANYKEY,int1], cnode, Array.from(chain), tlvl))()
    }
  } else {
    const sArr = await req.app.get('redis').compSearchM(cnode[0]); 
    if (sArr.length) {
      for (sneaker in sArr) {
        (async () => await NN_walkd(req, [sArr[sneaker].COMPANYKEY,sArr[sneaker].CLIENTKEY], cnode, Array.from(chain), tlvl))()
      }
    } else {
      console.log('no more links on this chain: ',cnode);
    }
  }
}

问题解答

1. 递归调用的async/await写法错误

你当前用立即执行async函数包裹递归调用的写法是错误的:

(async () => await NN_walkd(...))()

这种写法会创建一个未被等待的Promise,导致:

  • 主函数NN_walk中的await NN_walkd无法等待所有递归分支完成,就直接执行req.session.walk = null和cb()
  • 递归产生的Promise没有被捕获,一旦出错会触发未处理的Promise拒绝

正确写法:直接用await调用NN_walkd,因为它本身就是async函数,返回Promise:

// 替换原递归调用代码
await NN_walkd(req, [sObj.COMPANYKEY,int1], cnode, Array.from(chain), tlvl);

2. 解决compSearchK卡住的问题

首先修复查询函数的反模式

你的compSearchK和compSearchM都用了**new Promise嵌套async函数**的反模式,这完全没必要,async函数本身就返回Promise,嵌套写法会打乱异步流程,是卡住的潜在原因。

修复后的代码:

// 修复compSearchK
async compSearchK(ckey) {
  const sckey = await this._companyRepo.search()
    .where('COMPANYKEY').equals(ckey)
    .return.all();
  if (!sckey.length) return null;
  const ttrr = await this._companyRepo.fetch(sckey[0].entityId);
  return ttrr.toJSON();
}

// 修复compSearchM(用Promise.all并行优化效率)
async compSearchM(mkey) {
  const smkey = await this._companyRepo.search()
    .where('MASTERKEY').equals(mkey)
    .and('TBLNUM').equals(10)
    .return.all();
  if (!smkey.length) return [];
  return Promise.all(
    smkey.map(item => this._companyRepo.fetch(item.entityId).then(ttrr => ttrr.toJSON()))
  );
}

修复递归遍历的异步等待

原代码中用for...in遍历数组(这本身就不合适,for...in是遍历对象属性的),且没有等待所有递归调用完成,会导致大量未处理的Promise同时发起,可能耗尽Redis连接池或阻塞请求队列,进而卡住.search调用。

修改walk.js中的else分支:

else {
  const sArr = await req.app.get('redis').compSearchM(cnode[0]); 
  if (sArr.length) {
    // 用Promise.all等待所有递归分支完成,同时用map遍历数组
    await Promise.all(
      sArr.map(item => {
        return NN_walkd(req, [item.COMPANYKEY, item.CLIENTKEY], cnode, Array.from(chain), tlvl);
      })
    );
  } else {
    console.log('no more links on this chain: ',cnode);
  }
}

额外注意

  • 遍历数组优先用for...of或Array.map,避免用for...in
  • 递归遍历循环链时要注意检测循环,避免无限递归

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 16:01:34