Cordova Background Fetch ajax请求挂起、后续请求不启动问题求助
问题说明
当前使用cordova-plugin-background-fetch实现后台拉取用户通知、触发本地通知的逻辑时,出现AJAX请求一直处于pending状态、第二个请求无法发起的问题,原推测为插件30秒后台运行超时限制导致。
附控制台运行表现:
问题根因
- 两个AJAX请求并行发起,仅在第二个请求的回调中调用
BackgroundFetch.finish(taskId),第一个请求(获取通知未读数)完成后未触发任务结束标记,一旦系统触发超时回调直接终止后台运行,所有未完成的网络请求都会被挂起为pending状态 - 原代码在AJAX的error重试逻辑、done回调中通过
this.data.fetch获取BackgroundFetch实例的写法存在上下文丢失问题,多数场景下this不指向原AJAX配置对象,导致finish()方法无法被正确调用,系统持续等待任务结束信号,到达运行时限后直接冻结应用网络线程 - 两个并行请求叠加最多3次的重试逻辑,总耗时很容易超出iOS/Android为后台fetch分配的最长30秒运行窗口,触发系统强制终止逻辑
修复后代码
核心调整三点:将并行请求改为串行降低总耗时;修复上下文丢失问题,通过闭包直接获取实例与taskId;新增finally逻辑保证无论请求成功、失败、重试耗尽,都能正确调用finish()结束后台任务,避免占用系统后台额度。
async function fetchBackgroundNotices(){ const BackgroundFetch = window.BackgroundFetch; // 后台Fetch事件处理回调 const onEvent = async function(taskId) { console.log('[BackgroundFetch] 事件接收', taskId); const userdata = getUserData(); // 无用户数据时直接结束任务,避免空跑占用后台时间 if(userdata === null){ BackgroundFetch.finish(taskId); return; } try { // 封装Promise版AJAX请求,串行执行避免资源抢占 // 第一步:拉取通知未读数 const noticeCount = await new Promise((resolve, reject) => { let tryCount = 0; const retryLimit = 3; const sendRequest = () => { $.ajax({ url: `https://platform.followups.co.ke/api/business/notices/getcount/${userdata.agenciesid}`, dataType: 'json', method: "GET", success: res => resolve(res), error: () => { tryCount++; tryCount <= retryLimit ? sendRequest() : reject(new Error('获取通知未读数失败')); } }) } sendRequest(); }); embedNoticeCountBadge(noticeCount); // 第二步:拉取未读通知内容 const noticeList = await new Promise((resolve, reject) => { let tryCount = 0; const retryLimit = 3; const sendRequest = () => { $.ajax({ url: `https://platform.followups.co.ke/api/business/notices/getunread/${userdata.agenciesid}`, dataType: 'json', method: "GET", success: res => resolve(res), error: () => { tryCount++; tryCount <= retryLimit ? sendRequest() : reject(new Error('获取未读通知失败')); } }) } sendRequest(); }); pushNoticeMessages(noticeList); } catch (err) { console.log('[BackgroundFetch] 通知拉取异常', err); } finally { // 所有逻辑执行完必须调用finish,否则会被系统判定为后台异常耗电 BackgroundFetch.finish(taskId); } }; // 超时回调:到达系统允许的最大运行时间后立刻终止任务,不要执行额外操作 const onTimeout = function(taskId) { console.log('[BackgroundFetch] 运行超时', taskId); BackgroundFetch.finish(taskId); }; const status = await BackgroundFetch.configure({minimumFetchInterval: 15}, onEvent, onTimeout); console.log('[BackgroundFetch] 配置完成,状态码:', status); }
优化建议
- 不要将
minimumFetchInterval设置过小,iOS系统不会严格按照配置的15分钟间隔触发,会根据用户的App打开频率、设备电池状态自动调整触发频率,间隔设置过小反而会被系统判定为高耗电应用,降低后台触发优先级 - 后台运行时仅执行拉取必要数据、触发本地通知的轻量操作,不要执行除轻量本地存储外的重逻辑,总耗时控制在10秒以内最稳妥,避免触发30秒超时机制
cordova-plugin-background-fetch本身定位是低频轻量的后台数据同步,不适合高频实时通知场景,若业务对通知实时性要求高,建议采用替代方案
可选替代方案
- 优先接入系统级推送:iOS端用APNs、Android端用FCM或国内厂商推送通道,新通知由服务端直接推送到设备,不需要App后台轮询,可靠性最高、耗电最低
- 若无法接入官方推送,可以用
cordova-plugin-background-mode做后台保活,但需要注意Android高版本的后台限制,需要引导用户开启自启动、电池优化白名单权限,否则App会被系统强制杀死 - 实时性要求高的场景建议做前后台降级:App前台时用WebSocket长连接接收消息,退到后台后切换为系统推送,平衡体验和功耗
内容的提问来源于stack exchange,提问作者Stanley Ngumo
相关产品推荐
相关产品推荐

