如何对对象数组实现open状态优先前置、再按创建时间二级排序
多规则排序实现与代码问题修复
问题根源定位
你的代码拿不到正确排序结果,本质上不是排序逻辑写错了,而是异步执行时序错位导致你在数据完全加载前就执行了排序,叠加排序规则缺失,才会完全达不到预期效果,具体拆解:
- 时序问题:Firestore的
collection.get()是返回Promise的异步操作,原代码在最外层db.get().then()回调还没走完的位置,就同步执行了invoices.sort()、return操作,此时invoices数组从头到尾都是空的,不管你怎么写排序比较逻辑,结果都不会有任何变化。同时原代码嵌套的子集合发票查询没有统一的等待机制,子查询未完成时外层就提前退出执行,进一步放大了时序错位的问题。 - 排序规则缺失:原有的排序逻辑只实现了“open状态优先排前”的分组规则,同状态分组下没有追加
created字段的比较分支,自然没法实现二次排序。 - 冗余实现:你之前尝试的
Object.values(obj).includes("open")属于过度设计,我们明确知道状态存储在status字段上,直接读取该字段做判断即可,遍历整个对象值匹配的写法既增加性能消耗,还容易误匹配其他字段的文本值。
正确实现方案
调整点说明
- 用
async/await重构异步流程,收集所有嵌套查询的Promise,等所有发票数据完全加载进数组后,再执行排序操作 - 补全两层排序的比较逻辑:
- 第一优先级:先对比状态,open状态的发票排在非open状态前面
- 第二优先级:同一状态分组内,按
created字段时间倒序排列(最新创建的发票在前)
- 兼容Firestore的Timestamp类型
created字段,避免直接运算报错
修正后的完整代码
export const fetchInvoices = async function (firestore, currentUser) { const db = firestore .collection("customers") .doc(currentUser) .collection("subscriptions"); const invoices = []; // 等待所有订阅文档加载完成 const subscriptionSnapshot = await db.get(); // 收集每个订阅下的发票查询Promise,一次性等待所有查询完成 const invoiceRequests = subscriptionSnapshot.docs.map(subDoc => subDoc.ref.collection("invoices").get() ); const invoiceSnapshots = await Promise.all(invoiceRequests); // 遍历所有发票快照填充数组 invoiceSnapshots.forEach(snapshot => { snapshot.forEach(doc => { const data = doc.data(); invoices.push({ billing_reason: data.billing_reason, created: data.created, currency: data.currency, hosted_invoice_url: data.hosted_invoice_url, id: doc.id, invoice_id: doc.id, invoice_pdf: data.invoice_pdf, number: data.number, period_end: data.period_end, period_start: data.period_start, status: data.status, subtotal: data.subtotal, total: data.total }); }) }); // 多规则排序 const sortedInvoices = invoices.sort((a, b) => { // 第一优先级:open状态优先 const statusRank = (b.status === 'open') - (a.status === 'open'); if (statusRank !== 0) return statusRank; // 第二优先级:同状态下按创建时间倒序 const getCreatedTime = (timeVal) => { // 兼容Firestore Timestamp 类型、数值时间戳两种格式 if (typeof timeVal === 'number') return timeVal; return timeVal?.toMillis?.() ?? 0; } const timeA = getCreatedTime(a.created); const timeB = getCreatedTime(b.created); return timeB - timeA; }); return sortedInvoices; }
调用注意事项
因为函数本身改成了异步实现,调用fetchInvoices的时候必须搭配await关键字获取结果,不能直接同步读取返回值,否则拿到的还是Promise实例,不是实际的发票数组。
内容的提问来源于stack exchange,提问作者Jesus Jimenez
相关产品推荐
相关产品推荐

