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

fetch()抛出的Promise未捕获问题求助

原生Fetch Promise错误无法捕获问题排查

项目采用Yii2 PHP后端+Vue前端架构,自行开发了原生JS可复用API客户端类用于资源CRUD操作,期望通过中间件统一处理API响应。代码基于原生fetch()实现,在processRestResponse函数中,当收到非200响应时抛出错误,但所有添加的.catch()均无法捕获,始终出现“Uncaught (in promise)”错误。例如用户观看视频时调用UserMediaApi.create()更新user_media记录,后端报错时前端catch完全未触发,已尝试在调用链多处添加catch仍无效,不清楚是fetch、Promise还是错误捕获逻辑存在理解偏差,附上相关代码片段寻求解决思路。

通用API助手代码

const request = function(resource, options){
    return fetch(resource, options)
        .then(response => Promise.all([response, response.json()]));
}
const resourceUrl = function(){
    return this.autoPluralizeResource ?
        this.resourceName+'s' :
        this.resourceName;
}
const create = function(postData, options){
    const url = new URL(this.baseUrl+'/'+this.resourceUrl());
    if(!options){
        options = {};
    }
    options = {
        method: 'POST',
        body: JSON.stringify(postData),
        ...options,
    }

    if(!options.headers){
        options.headers = {};
    }
    options.headers = {
        'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
        "Content-Type": "application/json",
        ...options.headers
    }
    return this.request(url, options)
        .then(this.processRestResponse);
}
const processRestResponse = function([response, body]){
    if(!response.ok){
        if(response.status ==  422){
            if(Array.isArray(body)){
                let messages = [];
                body.forEach(validationError => {
                    messages.push(validationError.message);
                })
                throw {
                    name: response.status,
                    message: messages.join("\n")
                }
            }
        }
        throw {
            name: response.status,
            message: (body.message) ?
                body.message :
                response.statusText
        }
    }
    return Promise.all([response, body]);
}
export default {
    baseUrl: '',
    resourceName: '',
    autoPluralizeResource: true,

    resourceUrl: resourceUrl,
    request: request,
    create: create,

    processRestResponse: processRestResponse,
    handleErrorResponse: handleErrorResponse
};

UserMedia助手代码

import YiiApiHelper from './../../yiivue/YiiApiHelper.js';
export default {
    ...YiiApiHelper,
    baseUrl: window.location.origin+'/media/api/v1',
    resourceName: 'user-media',
    autoPluralizeResource: false
}

VimeoVideo.js代码

let updateWatchedStatus = function(watchedStatusId) {
    if(!props.userMedia){
        // --- 用户无该媒体记录,创建新记录
        return UserMediaApi.create({
                media_id: props.media.id,
                user_id: props.userId,
                data: {
                    [Helper.WATCHED_STATUS_KEY]: watchedStatusId
                }
            }).then(([response, body])  => {
                context.emit('userMediaUpdated', {userMedia: body});
                return body;
            }).catch(YiiApiHelper.handleErrorResponse);
    }

    // --- 用户已有记录,更新数据中的观看状态
    let data = {
        ...userMedia.value.data,
        [Helper.WATCHED_STATUS_KEY]: watchedStatusId
    }
    return UserMediaApi.update(props.media.id+','+props.userId, {
            data: data
        }).then(([response, body])  => {
            context.emit('userMediaUpdated', {userMedia: body});
            return body;
        }).catch(YiiApiHelper.handleErrorResponse);
}

核心问题排查与解决思路

  1. handleErrorResponse未定义导致二次错误
    通用API助手的export中声明了handleErrorResponse,但代码中并未实现该函数。当catch触发时调用YiiApiHelper.handleErrorResponse,实际是调用undefined,会抛出新的未捕获错误,这是出现“Uncaught (in promise)”的直接原因。

    • 解决:补全handleErrorResponse函数的实现,比如:
      const handleErrorResponse = function(error) {
          console.error('API错误:', error);
          // 可添加UI提示、日志上报等逻辑
          // 如果需要让上层继续捕获,可重新抛出错误
          throw error;
      }
      
  2. response.json()可能抛出未捕获错误
    request函数中直接返回Promise.all([response, response.json()]),若后端返回的响应不是合法JSON格式(比如错误时返回HTML页面),response.json()会抛出错误,该错误未被捕获,会导致Promise链reject且未被处理。

    • 解决:在request函数中添加catch处理JSON解析错误:
      const request = function(resource, options){
          return fetch(resource, options)
              .then(response => {
                  return response.json().then(body => [response, body])
                      .catch(parseError => {
                          // 处理JSON解析失败的情况
                          throw {
                              name: 'ParseError',
                              message: '响应格式不是合法JSON',
                              response: response,
                              parseError: parseError
                          };
                      });
              });
      }
      
  3. processRestResponse的上下文绑定问题
    create方法中使用.then(this.processRestResponse),此时processRestResponse的this并非API助手实例(而是全局window对象),虽然当前processRestResponse未用到this,但后续扩展代码时可能引发问题。

    • 解决:通过箭头函数或bind绑定上下文:
      return this.request(url, options)
          .then(responseData => this.processRestResponse(responseData));
      // 或者
      .then(this.processRestResponse.bind(this));
      
  4. 错误抛出的规范问题
    当前processRestResponse抛出的是普通对象,而非Error实例,虽然Promise会捕获,但部分环境或工具对非Error实例的错误处理可能存在差异,且无法获得错误栈信息,不利于调试。

    • 解决:改为抛出Error子类或实例:
      throw new Error(messages.join("\n"));
      // 或者自定义错误类
      class ApiError extends Error {
          constructor(status, message) {
              super(message);
              this.name = status.toString();
              this.status = status;
          }
      }
      // 使用时
      throw new ApiError(response.status, messages.join("\n"));
      

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 12:45:17