代码调试助手

345 浏览
30 试用
9 购买
Nov 19, 2025更新

本提示词帮助开发者快速定位代码中的问题,解释错误原因,并提供修正后的代码示例及优化建议,支持多语言代码调试与测试用例开发,提升调试效率和代码质量。

问题识别

  • 位置:
    • app.py 第 10 行:resp = session.get(url) 未使用 await
    • 随后 data = await resp.json() 实际上对协程对象调用了 .json(),触发 AttributeError: 'coroutine' object has no attribute 'json'
  • 报错:
    • RuntimeWarning: coroutine 'ClientSession.get' was never awaited
    • AttributeError: 'coroutine' object has no attribute 'json'
  • 并发问题:
    • 当前代码按顺序请求(即使修复 await 也会串行),未正确使用并发收集(gather),响应对象也未用上下文管理器确保回收。

原因解释

  • aiohttp.ClientSession.get 是一个异步协程,必须 await 才会返回响应对象(ClientResponse)。未 await 时返回的是协程对象,因此对其调用 .json() 会报 AttributeError
  • 未使用 async with session.get(...) 会导致响应未正确关闭,可能出现资源泄漏和连接未回收。
  • 未并发执行导致性能差;缺少超时和异常处理会在请求缓慢或非 JSON 响应时导致任务失败或卡住。

修复代码

说明:

  • 使用 async with session.get(...)await 获取响应,确保连接回收。
  • 使用 asyncio.gather 并发请求。
  • 加入 per-request 超时与异常处理:
    • 超时返回 None(满足场景2)。
    • 非 JSON 返回 {"error": "invalid json"}(满足场景3)。
    • 正常 JSON 时提取 slideshow.title,否则返回默认 "ok"(满足场景1)。
  • 默认将每个请求超时设为 3 秒,可根据需要调整。

可直接运行代码如下:

import asyncio
import aiohttp
from aiohttp import ClientError, ClientTimeout, ContentTypeError
from json import JSONDecodeError

async def fetch_one(session: aiohttp.ClientSession, url: str, req_timeout: float = 3.0):
    try:
        # 使用 per-request 超时,确保单个慢请求不会拖垮整体
        async with session.get(url, timeout=ClientTimeout(total=req_timeout)) as resp:
            if resp.status != 200:
                # 非 200 直接返回错误信息(可按需调整)
                return {"error": f"http {resp.status}"}
            try:
                # 解析 JSON;当 Content-Type 非 JSON 或 JSON 无效时抛异常
                data = await resp.json()
            except (ContentTypeError, JSONDecodeError, ValueError):
                return {"error": "invalid json"}
            # 正常 JSON:提取字段,默认 "ok"
            return data.get("slideshow", {}).get("title", "ok")
    except asyncio.TimeoutError:
        # 超时场景:返回 None
        return None
    except ClientError as e:
        # 网络层异常(连接错误、重置等)
        return {"error": str(e)}
    except Exception as e:
        # 兜底异常,避免任务失败中断整体
        return {"error": f"unexpected: {e}"}

async def fetch_all(urls, req_timeout: float = 3.0):
    # Session 级别超时稍大于单次请求,以防止聚合时误判
    session_timeout = ClientTimeout(total=max(req_timeout + 2.0, 5.0))
    async with aiohttp.ClientSession(timeout=session_timeout) as session:
        tasks = [asyncio.create_task(fetch_one(session, url, req_timeout)) for url in urls]
        return await asyncio.gather(*tasks)

if __name__ == "__main__":
    # 场景示例:
    # 1) 两个可用 URL 均返回 JSON(/json 和 /delay/2),默认 req_timeout=3.0 足以让 /delay/2 成功返回
    # 2) 若想模拟超时,将 req_timeout 调小,例如 1.0:asyncio.run(fetch_all(urls, req_timeout=1.0))
    # 3) 非 JSON 返回(/html)将得到 {"error":"invalid json"}
    urls = [
        "https://httpbin.org/json",
        "https://httpbin.org/delay/2",
        # "https://httpbin.org/html",  # 可取消注释测试场景3
    ]
    results = asyncio.run(fetch_all(urls))  # 可传 req_timeout 调整超时策略
    print(results)

简要改进说明与建议:

  • 关键修复:对 session.get 正确 await,并用 async with 确保连接释放。
  • 并发优化:用 asyncio.gather 并发执行,显著提升吞吐。
  • 稳健性:引入 per-request 超时与异常处理,避免单个请求拖垮整体;对非 JSON 提供明确错误结构。
  • 可扩展建议:大规模并发可加入并发限制(如 asyncio.Semaphore)和重试策略(对可重试错误进行指数退避)。

问题识别

  • 错误位置:const { users } = body; 之后直接调用 users.map(...)
  • 触发场景:当接口返回 {"data": []}{"data": null}{"users": null}、甚至网络错误时,usersundefined 或非数组,执行 map 触发 TypeError: Cannot read properties of undefined (reading 'map')
  • 其它隐患:
    • 未处理 fetch 超时、网络错误、非 2xx 状态码。
    • 未对响应结构做类型校验(users/data 非数组时未兜底)。
    • main 仅在函数抛出时捕获,但当前实现会在 users.map 处直接抛出。

原因解释

  • JS 中对 undefined 调用数组方法(如 map)会抛出 TypeError
  • 接口响应结构不固定(usersdata),且字段可能为 null 或类型不符;代码缺乏容错分支。
  • 网络层面未设置超时与重试策略,网络失败或慢响应均可能导致未捕获异常或长时间挂起。

修复代码

下面提供可直接运行的完整示例(Node 18+ 或浏览器环境),包含:

  • 兼容响应结构提取(优先 body.users,次选 body.data),类型校验与默认值兜底;
  • fetchWithTimeout(超时控制)与简单重试策略(网络错误与 5xx 重试,指数退避);
  • 单元测试示例(不依赖外部服务,基于依赖注入的 fetch mock);
  • 保证异常与网络错误时返回空数组,不向外抛出未捕获异常。
'use strict';

/**
 * 小工具函数:延迟
 */
const delay = (ms) => new Promise((res) => setTimeout(res, ms));

/**
 * 带超时与重试的 fetch 封装
 * - 超时:AbortController
 * - 重试:网络错误/超时或 5xx 时重试,指数退避
 */
async function fetchWithTimeout(
  url,
  fetchOptions = {},
  { timeout = 5000, retries = 2, retryDelay = 300 } = {}
) {
  let lastErr;
  for (let attempt = 0; attempt <= retries; attempt++) {
    const controller = new AbortController();
    const t = setTimeout(() => controller.abort(new Error('Fetch timeout')), timeout);

    try {
      const res = await fetch(url, { ...fetchOptions, signal: controller.signal });
      clearTimeout(t);

      // 非 2xx:5xx 可重试,4xx 直接返回
      if (!res.ok) {
        if (res.status >= 500 && attempt < retries) {
          await delay(retryDelay * Math.pow(2, attempt));
          continue;
        }
        return res; // 交给上层决定如何处理
      }

      return res;
    } catch (err) {
      clearTimeout(t);
      lastErr = err;
      if (attempt < retries) {
        await delay(retryDelay * Math.pow(2, attempt));
        continue;
      }
      throw lastErr;
    }
  }
  // 理论上不可达
  throw lastErr || new Error('Unknown fetch error');
}

/**
 * 规范化从响应体中提取用户列表:
 * - 优先 body.users,其次 body.data
 * - 仅当该字段为数组时返回,否则返回 []
 */
function normalizeUsers(body) {
  if (Array.isArray(body?.users)) return body.users;
  if (Array.isArray(body?.data)) return body.data;
  return [];
}

/**
 * 核心函数:获取活跃用户名(小写)
 * - 兼容 body.users 与 body.data
 * - 结构不符或网络问题 -> 返回 []
 * - 通过 options 支持 fetch 实现、超时与重试配置(便于测试)
 */
async function getActiveUsernames(
  api,
  {
    fetchImpl = fetch, // 允许注入 mock fetch 做单测
    timeout = 5000,
    retries = 2,
    retryDelay = 300,
  } = {}
) {
  try {
    // 使用我们封装的超时/重试 fetch
    const res = await (fetchImpl === fetch
      ? fetchWithTimeout(api, { method: 'GET' }, { timeout, retries, retryDelay })
      : // 若注入了自定义 fetch,则按原样调用(由测试或调用方决定重试/超时)
        fetchImpl(api, { method: 'GET' })
    );

    // 处理非 2xx
    if (!res || res.ok === false) {
      return [];
    }

    // 尝试解析 JSON
    let body;
    try {
      body = await res.json();
    } catch {
      return [];
    }

    // 兼容 users/data,并确保为数组
    const list = normalizeUsers(body);

    // 提取 name,转小写,非法值过滤
    const names = list
      .map((u) => (u && typeof u.name === 'string' ? u.name.toLowerCase() : ''))
      .filter(Boolean);

    return names;
  } catch {
    // 网络错误、超时或重试用尽 -> 返回空数组
    return [];
  }
}

/**
 * Demo 主函数(真实请求;若本地无服务,将打印 active: [])
 */
async function main() {
  const api = 'http://localhost:3000/users';
  const names = await getActiveUsernames(api, {
    timeout: 3000,
    retries: 2,
    retryDelay: 200,
  });
  console.log('active:', names);
}

// --------------- 单元测试示例(基于 fetch 依赖注入,无需外部依赖) ---------------

function createMockResponse(data, status = 200) {
  return {
    ok: status >= 200 && status < 300,
    status,
    async json() {
      return data;
    },
  };
}

function mockFetchFactory(routes) {
  // routes: { [url]: object | Error | function():object }
  return async function mockFetch(url, _opts) {
    if (!(url in routes)) return createMockResponse({}, 404);
    const v = routes[url];
    if (v instanceof Error) throw v;
    if (typeof v === 'function') return createMockResponse(await v(), 200);
    return createMockResponse(v, 200);
  };
}

function assertDeepEqual(actual, expected, msg) {
  const a = JSON.stringify(actual);
  const e = JSON.stringify(expected);
  if (a !== e) {
    throw new Error(`${msg}\nExpected: ${e}\nActual:   ${a}`);
  }
}

async function runTests() {
  const routes = {
    // 1) {"users":[{"name":"Ada"},{"name":"linus"}]} -> ["ada","linus"]
    'http://test/ok-users': { users: [{ name: 'Ada' }, { name: 'linus' }] },

    // 2) {"data":[{"name":"Grace"}]} -> ["grace"]
    'http://test/ok-data': { data: [{ name: 'Grace' }] },

    // 3a) {"data": null} -> []
    'http://test/data-null': { data: null },

    // 3b) 网络错误 -> []
    'http://test/network-error': new Error('network down'),
  };

  const mockFetch = mockFetchFactory(routes);

  const r1 = await getActiveUsernames('http://test/ok-users', { fetchImpl: mockFetch });
  assertDeepEqual(r1, ['ada', 'linus'], 'case 1 failed');

  const r2 = await getActiveUsernames('http://test/ok-data', { fetchImpl: mockFetch });
  assertDeepEqual(r2, ['grace'], 'case 2 failed');

  const r3a = await getActiveUsernames('http://test/data-null', { fetchImpl: mockFetch });
  assertDeepEqual(r3a, [], 'case 3a failed');

  const r3b = await getActiveUsernames('http://test/network-error', { fetchImpl: mockFetch });
  assertDeepEqual(r3b, [], 'case 3b failed');

  console.log('tests: all passed');
}

// 运行示例与测试(两者互不影响;测试不依赖真实网络)
main().catch((err) => console.error('fatal:', err));
runTests().catch((err) => {
  console.error('tests: failed ->', err);
});

简要改进说明与优化建议

  • 结构容错与默认值:
    • 使用 normalizeUsers 统一从 body.users/body.data 中提取并校验为数组,缺省返回 []。
    • 在映射阶段对 name 做类型判断,非法值过滤,避免再次抛错。
  • 网络稳健性:
    • 引入 fetchWithTimeout,支持超时(AbortController)与指数退避重试(网络错误/超时/5xx)。
    • 对非 2xx 响应、解析失败、网络异常统一返回 [],避免未捕获异常破坏主流程。
  • 可测试性:
    • getActiveUsernames 支持注入 fetch 实现,便于使用本地 mock 或 fetch-mock 做单测。
    • 提供自包含的 mock 示例与断言,覆盖题述三种测试条件。
  • 进一步建议:
    • 若需要区分错误类型,可在返回结构中携带 error 字段或使用 Result 风格(如 { ok: true, data: [...] })。
    • 在生产环境中记录失败原因(HTTP 状态、错误栈),以便排障与观测。

示例详情

解决的问题

帮助开发者快速定位代码中的问题,提供清晰的问题原因分析,并给出精准的修正建议,以提升代码调试效率和开发体验。

适用用户

初学编程的学生

帮助编程初学者快速理解错误,提供详细修正建议,从而加速他们的学习进步。

后端开发工程师

在复杂系统开发中快速定位代码问题,节省时间并专注于核心逻辑的实现。

测试工程师

高效处理开发中发现的错误代码,快速生成修复方案,优化测试工作流程。

特征总结

快速定位代码问题,帮助用户迅速找到并理解代码中的错误。
智能分析错误原因,为每个问题提供详细、易懂的解释。
自动生成修正后的代码,让用户轻松完成问题修复。
支持多种编程语言,覆盖主流开发场景,适配更多用户需求。
上下文敏感分析,针对具体问题提供精准的代码修复建议。
操作便捷,无需手动调整,只需输入问题代码即可获得全面解决方案。
减少调试时间,提升开发效率,助力用户专注于核心功能开发。
专业建议与修正方案兼具,可帮助新手快速提升代码能力。
灵活应用于开发、学习、测试等多种场景,让每位用户都能快速获益。

如何使用购买的提示词模板

1. 直接在外部 Chat 应用中使用

将模板生成的提示词复制粘贴到您常用的 Chat 应用(如 ChatGPT、Claude 等),即可直接对话使用,无需额外开发。适合个人快速体验和轻量使用场景。

2. 发布为 API 接口调用

把提示词模板转化为 API,您的程序可任意修改模板参数,通过接口直接调用,轻松实现自动化与批量处理。适合开发者集成与业务系统嵌入。

3. 在 MCP Client 中配置使用

在 MCP client 中配置对应的 server 地址,让您的 AI 应用自动调用提示词模板。适合高级用户和团队协作,让提示词在不同 AI 工具间无缝衔接。

AI 提示词价格
¥25.00元
先用后买,用好了再付款,超安全!

您购买后可以获得什么

获得完整提示词模板
- 共 551 tokens
- 5 个可调节参数
{ 编程语言 } { 错误描述 } { 代码内容 } { 调试目标 } { 测试条件 }
获得社区贡献内容的使用权
- 精选社区优质案例,助您快速上手提示词
限时免费

不要错过!

免费获取高级提示词-优惠即将到期

17
:
23
小时
:
59
分钟
:
59