{"id":15893914,"url":"https://github.com/chinanf-boy/explain-workerize","last_synced_at":"2026-01-17T18:37:53.454Z","repository":{"id":90548242,"uuid":"117846100","full_name":"chinanf-boy/explain-workerize","owner":"chinanf-boy","description":"explain workerize - run module in web worker 在worker运行一个模块 ","archived":false,"fork":false,"pushed_at":"2018-01-18T03:05:05.000Z","size":11,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-02T18:16:22.373Z","etag":null,"topics":["explain","explain-workerize","workerize"],"latest_commit_sha":null,"homepage":"https://github.com/developit/workerize","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chinanf-boy.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-01-17T14:18:00.000Z","updated_at":"2018-01-17T14:20:07.000Z","dependencies_parsed_at":"2023-07-18T11:00:08.858Z","dependency_job_id":null,"html_url":"https://github.com/chinanf-boy/explain-workerize","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/chinanf-boy/explain-workerize","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chinanf-boy%2Fexplain-workerize","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chinanf-boy%2Fexplain-workerize/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chinanf-boy%2Fexplain-workerize/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chinanf-boy%2Fexplain-workerize/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chinanf-boy","download_url":"https://codeload.github.com/chinanf-boy/explain-workerize/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chinanf-boy%2Fexplain-workerize/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28516195,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-17T18:28:00.501Z","status":"ssl_error","status_checked_at":"2026-01-17T18:28:00.150Z","response_time":85,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["explain","explain-workerize","workerize"],"created_at":"2024-10-06T08:14:04.208Z","updated_at":"2026-01-17T18:37:53.434Z","avatar_url":"https://github.com/chinanf-boy.png","language":"HTML","readme":"## workerize\n\n[![explain](http://llever.com/explain.svg)](https://github.com/chinanf-boy/Source-Explain)\n\n在Web Worker中运行一个模块。 \n\n作者是 [Preact- 小型React库的作者](https://github.com/developit/preact)\n\n[github source](https://github.com/developit/workerize)\n\n---\n\n尝试 `workderize`, 在 调试`console`\n\n``` js\nlet worker = window.workerize(`\n\texport function add(a, b) {\n\t\t// block for half a second to demonstrate asynchronicity\n\t\tlet start = Date.now();\n\t\twhile (Date.now()-start \u003c 500);\n\t\treturn a + b;\n\t}\n`);\n\n(async () =\u003e {\n\tconsole.log('3 + 9 = ', await worker.add(3, 9));\n\tconsole.log('1 + 2 = ', await worker.add(1, 2));\n})();\n```\n\n在本目录，启动`http`\n\n\u003e python\n\n```\npython -m SimpleHTTPServer\n```\n\n\u003e javascript\n\n```\nnpx http-server .\n```\n\n---\n\n## workerize\n\n因为只有一个文件\n\n[index.js](./workerize/src/index.js)\n\n---\n\n按照给出的例子,和作者描述\n\n- 将一个小型的专门构建的RPC实现捆绑到您的应用程序中\n\n- 如果导出的模块方法已经是异步的，那么签名是不变的\n\n- 支持同步和异步工作者功能\n\n- 与异步/等待美妙地工作\n\n---\n\n## [整体概括](#整体概括)\n\n- [workerize](#workerize)\n\n- [worker-\b管理](#worker-\b管理)\n\n- [开始运作-分配`add`函数到`worker`](#开始运作)\n\n- [setup-配置worker事件](#setup)\n\n- [工具函数-toCode-toCjs](#工具函数)\n\n---\n\n## workerize\n\n代码 22-34\n\n``` js\nexport default function workerize(code) {\n\tlet exports = {};\n\tlet exportsObjName = `__EXPORTS_${Math.random().toString().substring(2)}__`; // 随机 id\n\tif (typeof code==='function') code = `(${toCode(code)})(${exportsObjName})`; \n\t// 如果直接输出函数，从我们例子来看，先不管\n\tcode = toCjs(code, exportsObjName, exports);\n\t// 那么现在先去看 toCjs \u003c-----  1\n\n\tcode += `\\n(${toCode(setup)})(self, ${exportsObjName}, {})`;\n\t// \u003c--- 2 把 setup 函数加进来\n\tlet blob = new Blob([code], {\n\t\t\ttype: 'application/javascript'\n\t\t}),\n\t\turl = URL.createObjectURL(blob), // 变成了可以被加载的 Url\n\t\tworker = new Worker(url),\n\t\tcounter = 0,\n\t\tcallbacks = {};\n```\n\n- [toCode](#tocode)\n\n\u003e 函数变文本\n\n- [toCjs](#tocjs)\n\n\u003e 改造文本，记录函数\n\n- code += setup\n\n\u003e [到这一步JsBin \u003e code += setip](http://jsbin.com/rufuzeq/5/edit?js,console)\n\n- [Blob](#blob)\n\n\u003e Blob 对象表示不可变的类似文件对象的原始数据。[jsbin 例子\b](http://jsbin.com/rufuzeq/6/edit?js,console)\n\n- [Worker](#worker)\n\n\u003e `Worker()` 构造函数创建一个 Worker 对象，该对象执行指定的URL脚本。这个脚本必须遵守 同源策略 。\n\n---\n\n### worker-\b管理\n\n代码 35-44\n\n``` js\n\tworker.kill = signal =\u003e {\n\t\tworker.postMessage({ type: 'KILL', signal });\n\t\tsetTimeout(worker.terminate);\n\t}; // 关闭\n\tlet term = worker.terminate;\n\tworker.terminate = () =\u003e {\n\t\tURL.revokeObjectURL(url); // 丢 url\n\t\tterm(); // 触发本身结束命令\n    }; // \n    worker.rpcMethods = {};\n```\n\n---\n\n## 开始运作\n\n代码 84-95\n\n``` js\n\tsetup(worker, worker.rpcMethods, callbacks); // \u003c---- 1\n\tworker.call = (method, params) =\u003e new Promise( (resolve, reject) =\u003e {\n\t\tlet id = `rpc${++counter}`;\n\t\tcallbacks[id] = { method, resolve, reject };\n\t\tworker.postMessage({ type: 'RPC', id, method, params });\n\t});\n\t// exports 通过 toCjs 函数 获取到了 函数名\n\t// exports = {\n\t// \t'add' : true\n\t// }\n\tfor (let i in exports) {\n\t\t// i == 'add'\n\t\tif (exports.hasOwnProperty(i) \u0026\u0026 !(i in worker)) {\n\t\t\tworker[i] = (...args) =\u003e worker.call(i, args);\n\t\t}\n\t}\n\treturn worker;\n```\n\n- [setup](#setup)\n\n- `wroker[i]`\n\n还记得 我们例子的使用 `worker.add`\n\n``` js\n// usage\n(async () =\u003e {\n\tconsole.log('3 + 9 = ', await worker.add(3, 9)); // args 3,9\n\tconsole.log('1 + 2 = ', await worker.add(1, 2)); // args 1,2\n})();\n// index.js\nworker[i] = (...args) =\u003e worker.call(i, args); // args=\n```\n\n- `worker.call`\n\n\u003e 使用函数，通知 worker 内部, \n\n``` js\n\tworker.call = (method, params) =\u003e new Promise( (resolve, reject) =\u003e { // 变成了 异步/Promise\n\t\tlet id = `rpc${++counter}`; // 创建 唯一id\n\t\tcallbacks[id] = { method, resolve, reject }; // 放入本地缓存函数集\n\t\tworker.postMessage({ type: 'RPC', id, method, params }); // 第一次触发 onmessage 事件\n\t\t// type 自定义类型, id 唯一标签, method 函数, params 变量\n\t});\n```\n---\n\n### setup\n\n代码 45-83\n\n\u003e ctx == worker , rpcMethods == {}, callbacks == {}\n``` js\n\n\tfunction setup(ctx, rpcMethods, callbacks) {\n\t\t/*\n\t\tctx.expose = (methods, replace) =\u003e {\n\t\t\tif (typeof methods==='string') {\n\t\t\t\trpcMethods[methods] = replace;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tif (replace===true) rpcMethods = {};\n\t\t\t\tObject.assign(rpcMethods, methods);\n\t\t\t}\n\t\t};\n\t\t*/\n\t\tctx.addEventListener('message', ({ data }) =\u003e {\n\t\t\tif (data.type==='RPC') {\n\t\t\t\tlet id = data.id;\n\t\t\t\tif (id!=null) { \n\t\t\t\t\tif (data.method) { \n\t\t\t\t\t\tlet method = rpcMethods[data.method]; // 本地缓存-rpcMethods-中找出 method\n\t\t\t\t\t\tif (method==null) {\n\t\t\t\t\t\t\tctx.postMessage({ type: 'RPC', id, error: 'NO_SUCH_METHOD' }); \n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tPromise.resolve()\n\t\t\t\t\t\t\t\t.then( () =\u003e method.apply(null, data.params) )\n\t\t\t\t\t\t\t\t.then( result =\u003e { ctx.postMessage({ type: 'RPC', id, result }); }) \n\t\t\t\t\t\t\t\t// 触发ctx.onmessage 事件 \n\t\t\t\t\t\t\t\t// data == { type: 'RPC', id, result }); } \n\t\t\t\t\t\t\t\t.catch( error =\u003e { ctx.postMessage({ type: 'RPC', id, error }); }); \n\t\t\t\t\t\t\t\t// 触发ctx.onmessage 事件 \n\t\t\t\t\t\t\t\t// data == { type: 'RPC', id, error }); } \n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tlet callback = callbacks[id];  // 本地缓存-函数集-id\n\t\t\t\t\t\tif (callback==null) throw Error(`Unknown callback ${id}`);\n\t\t\t\t\t\tdelete callbacks[id];\n\t\t\t\t\t\tif (data.error) callback.reject(Error(data.error));\n\t\t\t\t\t\telse callback.resolve(data.result);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n```\n\n- `addEventListener('message', //...)` \n\n[`Worker`](#worker) - 属性 \n\n\u003e onmessage 一个事件监听函数，每当拥有 message 属性的 MessageEvent 从 worker 中冒泡出来时就会执行该函数。\n\n\u003e 事件的 data 属性存有消息内容。\n\n- `data.type === RPC`\n\n- ctx.postMessage\n\n\u003e 向 worker 的内部作用域内传递消息。触发 `ctx.onmessage` 事件\n\n因为 `ctx.postMessage` 在 `onmessage` 触发事件内部，所以[第一次触发](./README.md#L178)，不会来自内部\n\n---\n\n\n\n---\n\n## 工具函数\n\n### toCode\n\n``` js\nfunction toCode(func) {\n\treturn Function.prototype.toString.call(func);\n}\n```\n\n1. 定义\n``` js\nfunction add(){\n}\n```\n\n2. 运行\n\n\u003e Function.prototype.toString.call(add) // 变成 String\n\n3. 结果\n\n` \"function add(){}\"`\n\n---\n\n### toCjs\n\n\u003e - code : String\n\n``` js\n\t// String\n\texport function add(a, b) {\n\t\t// block for half a second to demonstrate asynchronicity\n\t\tlet start = Date.now();\n\t\twhile (Date.now()-start \u003c 500);\n\t\treturn a + b;\n\t}\n```\n\n\u003e - exportsObjName : String\n\n`__EXPORTS_${Math.random().toString().substring(2)}__`\n\n\u003e\u003e \"__EXPORTS_4706166142920267__\"\n\n\u003e - exports : Object `{}`\n\n\n``` js\nfunction toCjs(code, exportsObjName, exports) {\n\texportsObjName = exportsObjName || 'exports';\n\texports = exports || {};\n\tcode = code.replace(/^(\\s*)export\\s+default\\s+/m, (s, before) =\u003e {\n\t\t// export default function\n\t\texports.default = true; // 具有默认导出函数\n\t\t// before == ''\n\t\treturn `${before}${exportsObjName}.default = `;\n\t\t// code 将变成 __EXPORTS_4706166142920267__.default = function ...\n\t\t// 但是 这个例子 没有 default\n\t});\n\tcode = code.replace(/^(\\s*)export\\s+(function|const|let|var)(\\s+)([a-zA-Z$_][a-zA-Z0-9$_]*)/m, (s, before, type, ws, name) =\u003e {\n\t\texports[name] = true;\n\t\treturn `${before}${exportsObjName}.${name} = ${type}${ws}${name}`;\n\t});\n\t// code ==\n\t// `__EXPORTS_4706166142920267__.add = function add(a, b) {\n\t// \t// block for half a second to demonstrate asynchronicity\n\t// \tlet start = Date.now();\n\t// \twhile (Date.now()-start \u003c 500);\n\t// \treturn a + b;\n\t// }`\n\n\treturn `var ${exportsObjName} = {};\\n${code}\\n${exportsObjName};`;\n\n\t// return \n\t// var __EXPORTS_4706166142920267__ = {};\n\t// `__EXPORTS_4706166142920267__.add = function add(a, b) {\n\t// \t// block for half a second to demonstrate asynchronicity\n\t// \tlet start = Date.now();\n\t// \twhile (Date.now()-start \u003c 500);\n\t// \treturn a + b;\n\t// }`\n\t// __EXPORTS_4706166142920267__\n}\n```\n\n[本例子的-JsBin-](http://jsbin.com/rufuzeq/3/edit?js,console)\n\n### `code.replace`\n\n\u003e `replace()` 方法返回一个由替换值替换一些或所有匹配的模式后的新字符串。\n\n\u003e 模式可以是一个字符串或者一个正则表达式, 替换值可以是一个字符串或者一个每次匹配都要调用的函数。\n\n[mdn文档](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/replace)\n\n### 第一个\b正则式说明\n\n``` js\ncode.replace(/^(\\s*)export\\s+default\\s+/m,)\n\n``` \n\n\u003e `^` \n\n开头\n\n\u003e `\\s`\n\n匹配一个空白符，包括空格、制表符、换页符、换行符和其他 `Unicode` 空格。\n\n\u003e `( )`  (\\s)\n\n匹配 `\\s` 并且捕获匹配项。 这被称为捕获括号（capturing parentheses）。\n\n\u003e `*` \n\n匹配前面的模式 `\\s` 0 或多次。\n\n\u003e `export`\n\n直白匹配 `export`  文本\n\n\u003e `+` \n\n匹配前面的模式 `\\s` 1 或多次。\n\n\u003e /m\n\n多行; 将开始和结束字符（^和$）视为在多行上工作（也就是，分别匹配每一行的开始和结束（由 \\n 或 \\r 分割），而不只是只匹配整个输入字符串的最开始和最末尾处。\n\n---\n\n\n### 第二个正则式说明\n\n``` js\ncode.replace(/^(\\s*)export\\s+(function|const|let|var)(\\s+)([a-zA-Z$_][a-zA-Z0-9$_]*)\n```\n\n\u003e `(function|const|)`\n\n匹配 `function` 或 `const`\n\n\u003e [a-zA-Z$_]\n\n一个字符集合，也叫字符组。匹配集合中的任意一个字符。你可以使用连字符'-'指定一个范围。\n\n比如这个就是 小写的 `a到z` 大写 的`A到Z` `$` 和 `_`\n\n#### [更多正则式内容](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/RegExp)\n\n### return\n\n`var ${exportsObjName} = {};\\n${code}\\n${exportsObjName};`\n\n---\n\n## Blob\n\n[mdn文档](https://developer.mozilla.org/zh-CN/docs/Web/API/Blob)\n\n## worker\n\n[mdn文档](https://developer.mozilla.org/zh-CN/docs/Web/API/Worker)\n\n## 整体概括\n\n我们回顾一下，作者提到这个库的特点\n\n- 将一个小型的专门构建的`RPC`实现捆绑到您的应用程序中\n\n- 如果导出的模块方法已经是 `async` ，那么签名是不变的\n\n- 支持 `sync` 和 `async` 工作者功能\n\n- 与`async`/`await`美妙地工作\n\n---\n\n1. RPC 远程过程调用协议 ~~?~~  \n\n2. 我看到的签名，应该是 `唯一 id` 或者是 `exportsObjName` 这个随机值\n\n3. \n\t- `worker[i] = (...args) =\u003e worker.call(i, args);` 的使用\n\n\t- `worker.call = (method, params) =\u003e new Promise(` 的定义，必定是异步\n\n4. 上面也说了，`Promise` ,自然与async/await 美妙结合\n\n\u003e ---\n\n整体来说，作者活用了 [Blob](#blob) [worker](#worker) 两个api\n\n真的厉害:)\n\n---\n\n## 其他\n\n作者使用的 构建工具\n\n是 零配置的，power by [`rollup`](https://github.com/rollup/rollup) 的自己写的工具。\n\n- [rollup官网-优秀的中文](http://rollupjs.org/)\n\n- [microbundle-作者构建工具](https://github.com/developit/microbundle)","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchinanf-boy%2Fexplain-workerize","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchinanf-boy%2Fexplain-workerize","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchinanf-boy%2Fexplain-workerize/lists"}