Firefox ESR 91 Web Worker中requestAnimationFrame未定义如何解决
问题成因
requestAnimationFrame最初是仅暴露在主线程window上下文的API,Web Worker的独立全局作用域DedicatedWorkerGlobalScope早期并未默认提供该接口。
你使用的v91.10.0esr是火狐2022年发布的旧长期支持版本,该版本并未实装Worker上下文下的requestAnimationFrame支持——火狐从v113正式版开始才在Dedicated Worker全局作用域中暴露该API。安卓版Firefox、Edge、Chrome因为常规版本迭代速度快,更早实装了这一特性,因此同一段代码在这些环境中可以正常运行,在旧版Firefox ESR中会直接抛出引用不存在的错误。
解决方案
- 方案1:升级浏览器版本。如果业务场景允许终端用户升级浏览器,直接将Firefox升级至v113及以上版本,即可原生支持Worker内直接调用
requestAnimationFrame,无需修改现有业务代码。 - 方案2:添加兼容polyfill。如果需要覆盖旧版Firefox ESR等不支持Worker内
requestAnimationFrame的环境,可以通过主线程和Worker的消息通信实现兼容层,参考实现如下:
把Worker侧的兼容逻辑和业务代码拼接后传入Blob,主线程添加对应的消息转发逻辑,完整可运行代码如下:
该polyfill的回调触发时机、时间戳参数均和原生规范对齐,可覆盖绝大多数业务使用场景。// 拼接polyfill和原有Worker业务代码 const workerCode = ` // Worker内rAF兼容polyfill,需放在业务代码最前 if (typeof requestAnimationFrame === 'undefined') { const rafCallbacks = new Map(); let rafId = 0; self.addEventListener('message', (e) => { if (e.data?.type === '__raf_trigger__') { const callback = rafCallbacks.get(e.data.id); if (callback) { callback(e.data.timestamp); rafCallbacks.delete(e.data.id); } } }); self.requestAnimationFrame = (callback) => { const currentId = rafId++; rafCallbacks.set(currentId, callback); self.postMessage({ type: '__raf_request__', id: currentId }); return currentId; }; self.cancelAnimationFrame = (id) => { rafCallbacks.delete(id); self.postMessage({ type: '__raf_cancel__', id }); }; } // 原有Worker业务代码 requestAnimationFrame(() => self.postMessage('requestAnimationFrame in web-worker process')); `; const worker = new Worker( URL.createObjectURL( new Blob([workerCode], { type: "text/javascript" }) ) ); // 主线程rAF转发逻辑 const mainRafIdMap = new Map(); worker.addEventListener("message", e => { // 拦截rAF相关内部消息 if (e.data?.type === '__raf_request__') { const mainRafId = requestAnimationFrame((timestamp) => { mainRafIdMap.delete(e.data.id); worker.postMessage({ type: '__raf_trigger__', id: e.data.id, timestamp }); }); mainRafIdMap.set(e.data.id, mainRafId); return; } if (e.data?.type === '__raf_cancel__') { const targetRafId = mainRafIdMap.get(e.data.id); if (targetRafId) { cancelAnimationFrame(targetRafId); mainRafIdMap.delete(e.data.id); } return; } // 原有业务消息处理逻辑 console.log(e.data); }); requestAnimationFrame(() => console.log("requestAnimationFrame in main process"));
内容的提问来源于stack exchange,提问作者vanowm
相关产品推荐
相关产品推荐

