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

断开网络导致AngularJS(Ionic v1)进程中断问题排查

解决AngularJS(Ionic v1)批量上传中途断网时请求"僵尸状态"问题

这个问题的核心是:当网络中途断开时,部分$http请求会陷入pending状态(僵尸状态),既不进入成功回调也不进入失败回调,导致后续的数据库更新逻辑完全无法执行——哪怕照片已经成功传到了服务器。我来拆解原因并给出具体的解决方案:

为什么会出现"僵尸状态"?

浏览器在网络中断时,会默认挂起未完成的HTTP请求,等待网络恢复后重试。而AngularJS的$http服务本身没有默认超时机制,这就导致这些请求会一直处于pending状态,永远不会触发then或catch,你写的数据库更新代码自然就被卡住了。

另外你提到的online/offline监听器如果没有处理pending请求的逻辑,反而可能加剧这个问题——因为网络状态切换时,这些挂起的请求不会被主动终止。


解决方案1:给$http请求添加强制超时

最直接的办法是给每个上传请求设置超时时间,确保即使网络断了,请求也能在指定时间后进入catch回调,让你有机会处理后续逻辑。

修改你的sendPhotoInHttpRequest函数:

function sendPhotoInHttpRequest(payload) {
  // 定义5秒超时(可根据业务调整)
  const timeoutPromise = $timeout(() => {}, 5000);

  return $http({
    url: url,
    method: 'POST',
    data: JSON.stringify(payload),
    timeout: timeoutPromise // 绑定超时逻辑
  }).then(res => {
    // 上传成功,正常更新数据库
    return updateDatabaseSetPhotoAsSent(payload.id)
      .then(() => {
        increaseCounter(counterObj);
        return 'ok';
      });
  }).catch(err => {
    // 区分超时/网络错误和服务器异常
    if (err.status === -1 || err.message.includes('timeout')) {
      // 这里可以根据实际情况判断:如果确认照片已到服务器,直接更新数据库
      // (比如可以通过后续服务器接口校验,或者本地标记后同步)
      return updateDatabaseSetPhotoAsSent(payload.id)
        .then(() => {
          increaseCounter(counterObj);
          return 'ok (recovered from network issue)';
        });
    }
    // 其他错误继续抛出,不影响正常错误处理
    throw err;
  });
}

解决方案2:监听网络状态,主动取消pending请求

结合你已经配置的online/offline监听器,我们可以主动管理所有pending的请求,在网络断开时直接取消它们,避免请求一直挂起。

  1. 先添加一个HTTP拦截器,用来追踪所有pending请求:
// 在app.js中定义拦截器
app.factory('pendingRequestInterceptor', function($q) {
  const pendingRequests = [];

  return {
    request: function(config) {
      // 给每个请求绑定一个取消器
      config.cancelToken = $q.defer();
      pendingRequests.push(config);
      return config;
    },
    response: function(response) {
      // 请求完成后移除追踪
      const index = pendingRequests.indexOf(response.config);
      if (index !== -1) pendingRequests.splice(index, 1);
      return response;
    },
    responseError: function(rejection) {
      // 请求失败后移除追踪
      const index = pendingRequests.indexOf(rejection.config);
      if (index !== -1) pendingRequests.splice(index, 1);
      return $q.reject(rejection);
    },
    // 暴露取消所有pending请求的方法
    cancelAll: function() {
      pendingRequests.forEach(req => req.cancelToken.resolve('Network offline'));
      pendingRequests.length = 0;
    }
  };
});

// 注册拦截器
app.config(function($httpProvider) {
  $httpProvider.interceptors.push('pendingRequestInterceptor');
});
  1. 在offline事件中调用取消方法:
app.run(function($rootScope, pendingRequestInterceptor) {
  $rootScope.$on('$cordovaNetwork:offline', function() {
    // 网络断开时,取消所有pending请求
    pendingRequestInterceptor.cancelAll();
  });
});
  1. 最后修改上传函数,使用取消器:
function sendPhotoInHttpRequest(payload) {
  return $http({
    url: url,
    method: 'POST',
    data: JSON.stringify(payload),
    timeout: config.cancelToken.promise
  }).then(res => {
    return updateDatabaseSetPhotoAsSent(payload.id)
      .then(() => {
        increaseCounter(counterObj);
        return 'ok';
      });
  }).catch(err => {
    if (err.message === 'Network offline') {
      // 处理网络断开的情况,根据业务更新数据库
      return updateDatabaseSetPhotoAsSent(payload.id)
        .then(() => {
          increaseCounter(counterObj);
          return 'ok (canceled due to offline)';
        });
    }
    throw err;
  });
}

额外提醒:避免手动$q.defer反模式

你原来的代码里手动创建了defer对象,这其实是AngularJS中的反模式——$http本身已经返回Promise,直接链式调用then即可,不需要手动管理defer,这样代码更简洁也更不容易出错。


内容的提问来源于stack exchange,提问作者Edson Horacio Junior

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 10:13:52