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

如何并行执行返回不同类型结果的电商API请求方法?

电商API请求并行执行解决方案

我正在开发一款对接电商API的应用,已经写了大概30个对应API请求的类,现在想实现这些请求的并行执行。之前试过用List<Task>来做,但因为方法返回类型不明确,没成功。

示例请求类

public class GetOrderStatusList : IRequest<GetOrderStatusList.Response> {
    public class Status {
        [JsonPropertyName("id")]
        public int Id { get; set; }

        [JsonPropertyName("name")]
        public string Name { get; set; }
    }

    public class Response : Output {
        [JsonPropertyName("statuses")]
        public List<Status> Statuses { get; set; }
    }
}
public class GetProductsPrices : IRequest<GetProductsPrices.Response> {
    [JsonPropertyName("storage_id")]
    public string StorageId { get; set; }

    public class Product {
        public class Variant {
            [JsonPropertyName("variant_id")]
            public int VariantId { get; set; }

            [JsonPropertyName("price")]
            public decimal Price { get; set; }
        }

        [JsonPropertyName("product_id")]
        public int ProductId { get; set; }

        [JsonPropertyName("price")]
        public decimal Price { get; set; }

        [JsonPropertyName("variants")]
        public List<Variant> Variants { get; set; }
    }

    public class Response : Output {
        [JsonPropertyName("storage_id")]
        public string StorageId { get; set; }

        [JsonPropertyName("products")]
        public List<Product> Products { get; set; }
    }
}

基础接口与请求发送方法

public interface IRequest<TResponse> { }

public class Output {
    [JsonPropertyName("status")]
    public string Status { get; set; }

    [JsonPropertyName("error_message")]
    public string? ErrorMessage { get; set; }

    [JsonPropertyName("error_code")]
    public string? ErrorCode { get; set; }
}

public async Task<TResponse> SendRequestAsync<TResponse>(IRequest<TResponse> userRequest) where TResponse : Output {
        var client = new RestClient(_url);
        var method = GetRequestMethodName(userRequest);
        var request = CreateRequest(method, userRequest);
        var response = await ExecuteRequestAsync(client, request);
        var serializedResponse = JsonSerializer.Deserialize<TResponse>(response.Content);

        if( serializedResponse.Status == "ERROR") {
            throw new BaselinkerException(serializedResponse.ErrorMessage, serializedResponse.ErrorCode); 
        }

        return serializedResponse;
    } 

实现并行执行的具体方案

1. 统一任务类型,基于Output抽象

所有请求的Response都继承自Output,因此可以将所有SendRequestAsync调用包装为Task<Output>,统一放入列表管理:

// 构造请求实例列表
var requestInstances = new List<object>
{
    new GetOrderStatusList(),
    new GetProductsPrices { StorageId = "your-storage-id" }
};

// 转换为统一的Task<Output>列表
var tasks = requestInstances.Select(req => 
{
    return SendGenericRequest(req);
}).ToList();

// 等待所有任务完成
await Task.WhenAll(tasks);

// 逐个处理结果,转换为具体Response类型
foreach (var task in tasks)
{
    var result = await task;
    if (result is GetOrderStatusList.Response statusResponse)
    {
        // 处理订单状态数据
        foreach (var status in statusResponse.Statuses)
        {
            Console.WriteLine($"状态ID: {status.Id}, 名称: {status.Name}");
        }
    }
    else if (result is GetProductsPrices.Response priceResponse)
    {
        // 处理商品价格数据
        foreach (var product in priceResponse.Products)
        {
            Console.WriteLine($"商品ID: {product.ProductId}, 价格: {product.Price}");
        }
    }
}

2. 实现泛型辅助方法SendGenericRequest

需要通过反射调用泛型的SendRequestAsync,因为每个请求的TResponse类型不同:

private async Task<Output> SendGenericRequest(object request)
{
    var requestType = request.GetType();
    // 获取IRequest<TResponse>接口
    var requestInterface = requestType.GetInterfaces()
        .First(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IRequest<>));
    // 提取Response类型
    var responseType = requestInterface.GetGenericArguments()[0];

    // 反射调用SendRequestAsync<TResponse>
    var sendMethod = typeof(YourApiClientClass) // 替换为你的类名
        .GetMethod(nameof(SendRequestAsync))
        .MakeGenericMethod(responseType);
    
    var task = (Task<Output>)sendMethod.Invoke(this, new[] { request });
    return await task;
}

3. 可选:带类型标记的任务列表(无需反射创建任务)

如果不想在创建任务时用反射,可以提前绑定任务和对应的Response类型:

var taskWithTypes = new List<(Task Task, Type ResponseType)>
{
    (SendRequestAsync(new GetOrderStatusList()), typeof(GetOrderStatusList.Response)),
    (SendRequestAsync(new GetProductsPrices { StorageId = "your-storage-id" }), typeof(GetProductsPrices.Response))
};

// 等待所有任务完成
await Task.WhenAll(taskWithTypes.Select(t => t.Task));

// 处理结果
foreach (var (task, responseType) in taskWithTypes)
{
    // 提取任务结果
    var result = (Output)typeof(Task)
        .GetMethod("Result")
        .MakeGenericMethod(responseType)
        .Invoke(task, null);
    
    // 后续类型判断与处理逻辑和之前一致
}

4. 关键注意事项

  • 限流控制:电商API通常有请求频率限制,用SemaphoreSlim控制并行数:
var semaphore = new SemaphoreSlim(5); // 同时最多执行5个请求
var tasks = requestInstances.Select(async req =>
{
    await semaphore.WaitAsync();
    try
    {
        return await SendGenericRequest(req);
    }
    finally
    {
        semaphore.Release();
    }
}).ToList();
  • 异常处理:Task.WhenAll会在任一任务抛出异常时立即终止,建议逐个检查任务状态或者在SendRequestAsync中捕获部分异常,避免单个请求失败导致全部任务中断。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 21:40:46