ZBLOG

如何通过 postMessage/onmessage 将 WebWorker 的请求转发到主线程,由主线程统一处理。

要通过 postMessageonmessage 将 WebWorker 中的 fetch 请求转发到主线程处理,需要以下步骤:


1. 核心思路

  1. 劫持 WebWorker 的 fetch:在 Worker 脚本中覆盖原生 fetch,改为通过 postMessage 将请求参数发送给主线程。
  2. 主线程代理请求:主线程监听 Worker 的消息,收到请求后用自己的 fetch 发起真实请求,再将结果返回给 Worker。
  3. Worker 接收响应:Worker 监听主线程返回的结果,模拟原生 fetch 的 Promise 行为。

2. 完整实现代码

(1) OpenResty Lua 注入脚本

修改返回的 HTML,动态替换 Worker 的原始逻辑:

location /your-path {
    body_filter_by_lua_block {
        local inject_code = [[
<script>
// Main thread logic
(function() {
    // Store original Worker constructor
    const OriginalWorker = window.Worker;

    // Override Worker to inject our hook
    window.Worker = function(workerUrl) {
        // Step1: Fetch and modify the worker script
        const modifiedScript = fetch(workerUrl)
            .then(res => res.text())
            .then(scriptText => {
                // Inject our hook code into the worker script
                return `
// ---- Injected Hook Start ----
const originalFetch = fetch;
fetch = function(input, init) {
    return new Promise((resolve, reject) => {
        // Send request details to main thread
        self.postMessage({
            type: '__FETCH_PROXY_REQUEST__',
            input: input,
            init: init
        });

        // Listen for response from main thread
        self.addEventListener('message', function handler(e) {
            if (e.data.type === '__FETCH_PROXY_RESPONSE__' && e.data.requestId === this.requestId) {
                self.removeEventListener('message', handler);
                if (e.data.error) {
                    reject(new Error(e.data.error));
                } else {
                    resolve(new Response(e.data.body, e.data.options));
                }
            }
        }.bind({ requestId: performance.now() + Math.random() }));
    });
};
// ---- Injected Hook End ----
${scriptText}`;
            });

        // Step2: Create a blob URL for the modified worker
        const blobUrl = modifiedScript.then(script => {
            const blob = new Blob([script], { type: 'application/javascript' });
            return URL.createObjectURL(blob);
        });

        // Step3: Create the worker with modified script
        return blobUrl.then(url => new OriginalWorker(url));
    };

    // Handle worker's fetch requests in main thread
    navigator.serviceWorker?.controller?.addEventListener('message', async (e) => {
        if (e.data.type === '__FETCH_PROXY_REQUEST__') {
            try {
                const response = await fetch(e.data.input, e.data.init);
                const body = await response.text();
                e.source.postMessage({
                    type: '__FETCH_PROXY_RESPONSE__',
                    requestId: e.data.requestId,
                    body: body,
                    options: {
                        status: response.status,
                        headers: Object.fromEntries(response.headers.entries())
                    }
                });
            } catch (error) {
                e.source.postMessage({
                    type: '__FETCH_PROXY_RESPONSE__',
                    requestId: e.data.requestId,
                    error: error.message
                });
            }
        }
    });
})();
</script>
]]
        
        -- Inject before </body>
        local body = ngx.arg[1]
        if body then
            ngx.arg[1] = string.gsub(body, "</body>", inject_code .. "</body>")
        end
    }
}

(2) WebWorker 原始代码(被修改前)

假设原始的 Worker (worker.js)是这样的:

// Original worker.js without hook 
self.onmessage = function(e) { 
    fetch("https://api.example.com/data") 
       .then(r => r.json()) 
       .then(data => postMessage(data)) 
}

(3) Hook后的实际执行流程

  1. Worker调用 fetch() → 被劫持为向主线程发送消息。
  2. Main Thread收到消息 → 用真实 fetch() 发起请求。
  3. Main Thread返回结果 → 触发Worker内的Promise解析。

3. Key Points

  • 唯一ID标识请求:用 performance.now() + Math.random()生成唯一ID匹配请求/响应。
  • Response对象模拟:在主线程构造与原生Response结构相同的对象。
  • 错误处理:捕获网络错误并通过相同通道返回。

4. Limitations

  • Headers限制:部分只读Headers可能需要特殊处理。
  • Streaming响应:如果需要处理流式响应需额外逻辑。

需要更复杂的功能可以进一步扩展消息协议!


内容由零声教学AI助手提供,问题来源于学员提问

本站部分文章来源于网络,版权归原作者所有,如有侵权请联系站长删除。
转载请注明出处:https://golang.0voice.com/?id=23195

分享:
扫描分享到社交APP
上一篇
下一篇
发表列表
游客 游客
此处应有掌声~
评论列表

还没有评论,快来说点什么吧~

联系我们

在线咨询: 点击这里给我发消息

微信号:3007537140

上班时间: 10:30-22:30

关注我们
x

注册

已经有帐号?