Array.splice在异步函数中偶尔未按预期工作的问题排查
Array.splice在异步函数中偶尔未按预期工作的问题排查
看起来你遇到了一个挺棘手的偶发bug——明明逻辑上应该被移除的coverage偶尔会留在数组里,这种时灵时不灵的问题确实头疼!我来帮你梳理下可能的原因和对应的解决思路:
一、先排查最可能的偶发诱因:日期处理或类型不匹配
你的splice触发条件里有两个关键的时间判断,还有一个ID匹配,这两个地方很容易因为数据细节出问题:
- 日期格式未定义或不匹配:代码里用到了
format变量但没看到定义,如果format值不对,moment(auth.start, format)可能解析出无效的日期对象,导致时间判断isSameOrBefore/isSameOrAfter返回意外结果,进而跳过splice。建议先在函数开头明确定义format(比如const format = 'YYYY-MM-DD';),同时用moment.isValid()验证解析后的日期是否有效。 - ID类型不一致:如果
coverages[j].id是字符串类型,而auth.coverageId是数字类型,===判断会返回false,自然不会执行splice。这种情况只会在部分数据上出现,导致偶发bug。可以统一把两个值转成相同类型(比如都转成字符串:String(coverages[j].id) === String(auth.coverageId)),或者在数据查询阶段就统一类型。
二、遍历方式的潜在问题:for...in不适用于数组
你用了for (let i in auths)来遍历数组,for...in是为遍历对象属性设计的,它会遍历数组的所有可枚举属性(包括原型链上的),如果auths数组被意外添加了非索引属性,可能会导致遍历顺序错乱或遗漏元素。建议换成更可靠的数组遍历方式:
// 方式1:for...of 直接遍历元素 for (const auth of auths) { // 你的逻辑代码 } // 方式2:普通for循环遍历索引 for (let i = 0; i < auths.length; i++) { const auth = auths[i]; // 你的逻辑代码 }
三、异步遍历+原地修改数组的风险
你的循环里有await this.db.Patients.getByPatientPayer(auth.coverageId),这会让每次循环迭代之间释放事件循环(虽然单线程下不会有其他线程修改数组,但逻辑上的不连续可能让你难以排查问题)。加上splice是原地修改数组,在遍历过程中修改数组本身就容易引发意外行为。
最好的解决方式是按照你TODO里的提示,用Array.filter重构这部分逻辑,避免原地修改数组,改用纯函数式的方式生成新数组:
// 先预处理auths,收集所有需要排除的coverage信息 const targetMoment = moment(date); // 提前创建目标日期的moment对象,避免重复创建 const excludedCoverageInfo = auths .filter(auth => auth.coverageId) // 只保留有coverageId的auth .map(auth => ({ id: auth.coverageId, start: moment(auth.start, format), finish: moment(auth.finish, format) })); // 用filter生成新的无auth的coverage数组 const coveragesWithoutAuths = coverages.filter(coverage => { // 检查当前coverage是否在排除列表中 const isExcluded = excludedCoverageInfo.some(info => String(info.id) === String(coverage.id) && info.start.isSameOrBefore(targetMoment, "day") && info.finish.isSameOrAfter(targetMoment, "day") ); return !isExcluded; });
这种方式逻辑更清晰,不会修改原数组,也避免了异步遍历和原地修改的冲突,可靠性更高。
四、额外的调试建议
为了定位偶发问题,可以在splice的条件判断里加日志,记录每次判断的结果:
// 在判断条件前添加日志 const isMatchId = coverages[j].id === auth.coverageId; const isStartValid = moment(auth.start, format).isSameOrBefore(moment(date).format(format), "day"); const isFinishValid = moment(auth.finish, format).isSameOrAfter(moment(date).format(format), "day"); console.log(`Checking coverage ${coverages[j].id} against auth ${auth.coverageId}:`, {isMatchId, isStartValid, isFinishValid}); if (isMatchId && isStartValid && isFinishValid) { coverages.splice(j, 1); break; }
这样当bug再次出现时,你可以从日志里看到是哪个条件不满足,快速定位问题根源。
备注:内容来源于stack exchange,提问作者Muirik
相关产品推荐
相关产品推荐

