React文件上传:XMLHttpRequest抛出的错误无法被调用函数捕获的问题
问题描述
我正在ReactJS中开发一个允许用户向图片应用上传文件的功能。后端会验证文件类型,确保其为支持的格式(而非.txt、.bat等非图片格式)。若用户上传不支持的类型,服务器会返回HTTP 415错误「Unsupported Media Type」。我能在发送请求的sendCSRFRequest函数中获取该状态码,代码如下:
export function sendCSRFRequest(formData, targetURL) { const request = new XMLHttpRequest(); // check for HTTP response status request.onreadystatechange = () => { if (request.readyState === 4) { if (request.status === 200) { console.log("Upload successful!"); } else if (request.status === 415) { throw new Error("Can't upload that!"); } } }; request.open('POST', targetURL); request.setRequestHeader("X-CSRFToken", getCookie('csrftoken')); request.send(formData); };
这段代码能成功识别415错误并抛出「Can't upload that!」的错误信息,但我希望将该错误传递给调用函数,以便进行条件渲染并在UI中向用户提示错误。调用函数代码如下:
async function handleUpload(e, files, setFiles, setUploadFailed) { e.preventDefault() // do not redirect page const formData = new FormData(); files.forEach(file => {formData.append('file', file)}); try { sendCSRFRequest(formData, 'api/upload/'); // Function shown above } catch (e) { console.log("Found an error!"); console.error(e); setUploadFailed(true); // Triggers an alert in the UI } await setFiles([]); // If upload is successful, clear out state "files" };
但第二个函数无法捕获第一个函数抛出的错误,我尝试了多种写法(调整try/catch、添加/移除await、改用catch链式调用等)都无效。请问为何sendCSRFRequest中抛出的错误无法冒泡到调用函数?需要如何修改才能让错误被调用函数捕获?
原因分析
- 错误抛出在
onreadystatechange回调函数里,这个回调是异步执行的,属于XMLHttpRequest的异步事件回调,它的执行上下文和调用sendCSRFRequest的上下文完全分离。 - 同步的
try/catch只能捕获同一执行栈里的同步错误,无法捕获异步回调中抛出的错误,所以handleUpload里的try/catch根本抓不到这个错误。
修改方案
把sendCSRFRequest改造成返回Promise的函数,利用Promise的异步错误处理机制来传递错误:
第一步:重构sendCSRFRequest为Promise风格
export function sendCSRFRequest(formData, targetURL) { return new Promise((resolve, reject) => { const request = new XMLHttpRequest(); request.onreadystatechange = () => { if (request.readyState === 4) { if (request.status === 200) { console.log("Upload successful!"); resolve(); // 请求成功时resolve } else if (request.status === 415) { reject(new Error("Can't upload that!")); // 415错误时reject } else { // 处理其他HTTP错误状态 reject(new Error(`Request failed with status ${request.status}`)); } } }; // 处理网络层面的错误(比如连接失败) request.onerror = () => { reject(new Error("Network error occurred")); }; request.open('POST', targetURL); request.setRequestHeader("X-CSRFToken", getCookie('csrftoken')); request.send(formData); }); };
第二步:修改handleUpload函数,使用await配合try/catch
async function handleUpload(e, files, setFiles, setUploadFailed) { e.preventDefault(); const formData = new FormData(); files.forEach(file => {formData.append('file', file)}); try { await sendCSRFRequest(formData, 'api/upload/'); // 用await等待Promise完成 await setFiles([]); // 仅上传成功时清空文件列表 } catch (e) { console.log("Found an error!"); console.error(e); setUploadFailed(true); } };
关键说明
- 用Promise包裹XMLHttpRequest的异步逻辑,成功时调用
resolve,失败(包括415错误、网络错误)时调用reject,让异步错误能通过await+try/catch捕获。 - 将清空文件列表的逻辑移到
try块内,避免上传失败后误清空用户选择的文件。 - 新增
request.onerror处理网络异常,覆盖更多错误场景。
内容的提问来源于stack exchange,提问作者cdyswnnr
相关产品推荐
相关产品推荐

