如何获取XMLHttpRequest的event.loaded并dispatch到state实现网速测试
问题原因
你当前代码拿不到正确下载大小的核心问题有3个:
onprogress是事件绑定的回调函数,函数内部的return不会向外传递值,你写return event.loaded没有任何实际作用- 你在请求完成时
resolve(xhr.onprogress),传递的是回调函数本身,不是下载过程中记录的字节数值 - 没有单独的变量持久化存储每次进度回调返回的
event.loaded,请求结束时自然拿不到最终的总下载字节数
修正实现
你只需要在XHR请求内部维护一个存储最新下载字节数的变量,每次onprogress触发时更新这个变量,同时可以直接dispatch进度到store做实时速度计算,等请求完成时把最终的字节数resolve出去即可,修正后的完整代码如下:
const startSpeedTestAsync = () => async (dispatch: Dispatch) => { const url = config.SPEED_TEST; dispatch(setLoadTest(true)); const startTime = new Date().getTime(); dispatch(startSpeedTest({ startTime, isShowInfo: false })); let finalLoaded = 0; const endRequest = (error?: unknown) => { const endTime = new Date().getTime(); dispatch(setEndTime(endTime)); dispatch(calcSpeed(error ? 0 : finalLoaded)); dispatch(stopSpeedTest({ isLoadTest: false, isShowInfo: true })); }; const sendHttpRequest = (method: string, currentUrl: string) => { const promise: Promise<number> = new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.timeout = 5000; xhr.onprogress = function (event) { finalLoaded = event.loaded; // 如需实时计算瞬时网速,可在此处派发进度action // dispatch(updateSpeedProgress({ loaded: event.loaded, time: Date.now() })); console.log('loaded: ', event.loaded); }; xhr.onreadystatechange = function () { if (xhr.readyState !== 4) return; // 请求完成后判断状态码,2xx代表正常响应 if (xhr.status >= 200 && xhr.status < 300) { resolve(finalLoaded); } else { reject(new Error(`Request failed with status ${xhr.status}`)); } }; // 绑定超时、错误回调,避免Promise挂起 xhr.ontimeout = () => reject(new Error('Request timeout')); xhr.onerror = () => reject(new Error('Network error')); xhr.open(method, currentUrl); xhr.send(); }); return promise; }; sendHttpRequest('GET', url) .then((loadedSize) => { console.log('最终下载字节数:', loadedSize); endRequest(); }) .catch((err) => { console.error('测速失败:', err); endRequest(err); }); };
注意事项
- 测速使用的资源建议选大小在1MB以上的静态文件,文件过小会导致
onprogress只触发1-2次,计算出的网速误差极大 - 如果需要更精准的实时网速,可以在
onprogress回调里记录每次触发的时间戳,用两次进度回调的字节差 / 时间差计算瞬时速度,比总大小/总时间的结果更贴近真实体验 - 测速资源需要配置跨域响应头,否则XHR请求会被浏览器跨域策略拦截,无法正常触发进度事件
内容的提问来源于stack exchange,提问作者Dima Karpov
相关产品推荐
相关产品推荐

