React TypeScript实现DynamoDB轮询异常:仅执行一次且抛TypeError
问题排查与解决方案
问题描述
需要实现React+TypeScript轮询功能:每30秒调用REST API读取DynamoDB记录,直到记录的Status字段变为Success。当前代码仅执行一次查询,且抛出Uncaught (in promise) TypeError: t is not a function错误。
错误原因分析
- 轮询函数参数类型不匹配:
poll函数要求第一个参数是返回Promise的函数,但你直接执行了callPolling(resJson),把执行后的Promise对象传了进去,而非函数本身。第一次执行await fn()时,相当于尝试调用一个Promise对象,自然触发“不是函数”的错误。 - API请求函数语法错误:
callPolling内使用了await但未标记为async;同时response变量未声明,会成为全局变量,存在风险。 - 未解析API响应:
fetch返回的是Response对象,必须调用.json()解析响应体,否则无法获取body.Status字段。
修复后的基础轮询代码
// 封装等待逻辑为Promise const wait = async function (ms = 1000): Promise<void> { return new Promise((resolve) => setTimeout(resolve, ms)); }; // 轮询核心函数:明确类型定义 const poll = async function ( fn: () => Promise<{ body: { Status: string } }>, fnCondition: (result: { body: { Status: string } }) => boolean, ms: number ): Promise<{ body: { Status: string } }> { let result = await fn(); // 未满足结束条件时持续轮询 while (fnCondition(result)) { await wait(ms); result = await fn(); } return result; }; // 修正后的API请求函数 async function callPolling(resJson: any): Promise<{ body: { Status: string } }> { try { const response = await fetch(url, { method: 'POST', body: JSON.stringify(request_data), headers: { 'Content-Type': 'application/json' }, }); // 解析响应体为JSON return await response.json(); } catch (e: unknown) { console.error('轮询请求失败:', e); throw e; // 抛出错误让上层处理,或根据业务返回默认值 } } // 启动轮询:传入函数本身而非执行结果 async function startPolling() { try { // 条件改为"状态不是Success时继续轮询" const validate = (result: { body: { Status: string } }) => result.body.Status !== 'Success'; const finalResult = await poll(() => callPolling(resJson), validate, 30000); console.log('轮询完成,最终结果:', finalResult); // 这里处理成功后的逻辑(如更新组件状态) } catch (e) { console.error('轮询过程出错:', e); } } // 在React组件中可通过useEffect启动 // useEffect(() => { // startPolling(); // }, []);
React环境下的更优实现(避免内存泄漏)
React组件卸载后,未清理的定时器会导致内存泄漏,推荐结合useEffect和useRef管理轮询:
import { useEffect, useRef } from 'react'; const PollingComponent = () => { const intervalRef = useRef<NodeJS.Timeout | null>(null); // 封装API请求 const fetchRecord = async (resJson: any) => { try { const response = await fetch(url, { method: 'POST', body: JSON.stringify(request_data), headers: { 'Content-Type': 'application/json' }, }); return await response.json(); } catch (e) { console.error('请求失败:', e); throw e; } }; // 轮询逻辑 const startPolling = async () => { const checkStatus = async () => { try { const result = await fetchRecord(resJson); if (result.body.Status === 'Success') { console.log('轮询完成:', result); // 停止轮询 if (intervalRef.current) { clearInterval(intervalRef.current); intervalRef.current = null; } // 执行成功后的业务逻辑(如更新state) } } catch (e) { console.error('轮询检查失败:', e); // 可选择是否继续轮询,或终止 } }; // 先立即执行一次,再启动定时轮询 await checkStatus(); intervalRef.current = setInterval(checkStatus, 30000); }; useEffect(() => { startPolling(); // 组件卸载时清理定时器 return () => { if (intervalRef.current) clearInterval(intervalRef.current); }; }, []); return <div>轮询中...</div>; }; export default PollingComponent;
关键注意事项
- 必须处理请求错误,避免单次失败导致整个轮询终止(可根据业务选择是否继续尝试)。
- React组件中务必清理定时器,防止内存泄漏。
- 尽量明确TypeScript类型,减少
any的使用,提升代码健壮性。
内容的提问来源于stack exchange,提问作者Sudha N
相关产品推荐
相关产品推荐

