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

Promise并发场景下Array.find()方法未生效问题排查

问题根因
  • 匹配逻辑字段写串了:你在savedProducts.find()里写的判断条件是t.id === e.id,这里t是存在缓存里的产品数据,e是当前处理的订单数据,产品ID和订单ID是两个完全不相关的字段,永远不可能相等,所以哪怕缓存里已经有对应产品,find也永远匹配不到,temp始终是undefined。
  • 空数组调用遍历方法不会执行回调:你在find回调里写的console.log没输出,根本不是数组方法没触发,是第一批并发任务启动的时候,savedProducts刚被初始化成空数组——所有JS数组的遍历方法(find/forEach/map等)在空数组上调用时,根本不会执行传入的回调函数,自然打不出日志。等第一批请求把产品数据push进数组之后,又因为前面写的判断条件字段错了,永远匹配不上,才会让你觉得数组方法完全没工作。
  • 并发竞态导致缓存失效:就算你把字段改对,现在的逻辑也挡不住重复请求。PromisePool开了5个并发,如果几个订单刚好对应同一个产品,这几个任务会同时查到缓存为空,同时发一样的产品查询请求,缓存根本拦不住。
  • 缓存生命周期不对:你把savedProducts定义在call方法里面,每次调用call都会新建一个空数组,相当于每次请求都从零开始攒缓存,跨请求的缓存完全不生效。
修复方案
  • 把find的判断条件改成t.id === e.product_id,用订单上存的产品ID和缓存里的产品ID做匹配。
  • 加一个Map存正在请求中的产品Promise,同一个产品ID如果已经在发请求了,就直接等这个请求返回,不要重复发,解决并发下的竞态问题。
  • 如果需要跨多次接口调用复用产品缓存,就把缓存数组、存请求中Promise的Map定义成类的属性,不要每次进call方法都重置;如果只需要单次call请求内去重,定义在方法里也可以,但必须加请求中状态的Map。

修复后的参考代码:

class OrderService {
    // 类属性,跨call调用持久化缓存,不需要跨调用缓存可以挪到call方法内
    private savedProducts: ProductFullRes[] = [];
    // 记录正在请求中的产品Promise,解决并发竞态
    private pendingProductMap = new Map<number | string, Promise<ProductFullRes>>();

    async call(p: UserOrdersParams): Promise<OrderWithCheckoutWithPagination> {
        try {
            const orders = await this.orderRepo.getOrders({page: p.page, perPage: p.perPage});
            const {results} = await PromisePool
                .for(orders.orders)
                .withConcurrency(5)
                .process(async (e) => {
                    // 先查已经落地的缓存
                    const cachedProduct = this.savedProducts.find(t => t.id === e.product_id);
                    let productRequest: Promise<ProductFullRes>;
                    
                    if (cachedProduct) {
                        productRequest = Promise.resolve(cachedProduct);
                    } else {
                        // 查有没有正在发的相同请求
                        const pendingRequest = this.pendingProductMap.get(e.product_id);
                        if (pendingRequest) {
                            productRequest = pendingRequest;
                        } else {
                            // 没有缓存也没有在途请求,发起新请求
                            productRequest = this.productRepo.getProductById(e.product_id);
                            this.pendingProductMap.set(e.product_id, productRequest);
                            // 请求完成后存入缓存,清掉在途记录
                            productRequest.then(product => {
                                this.savedProducts.push(product);
                                this.pendingProductMap.delete(e.product_id);
                            })
                        }
                    }

                    const temp = await productRequest;
                    const checkout = await this.orderRepo.getOrderCheckout(e.id);
                    return {
                        order: e,
                        checkout: checkout,
                        product: temp
                    } as OrdersWithCheckout;
                })
            return {
                orders: results,
                pagination: {
                    meta: orders.meta,
                    links: orders.links
                }
            }
        } catch (e) {
            return Promise.reject(new NetworkFailure((e as Exception).codeMessage, (e as Exception).code));
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 19:09:22