Giter Site home page Giter Site logo

chinanf-boy / explain-workerize Goto Github PK

View Code? Open in Web Editor NEW
0.0 2.0 0.0 11 KB

explain workerize - run module in web worker 在worker运行一个模块

Home Page: https://github.com/developit/workerize

HTML 53.18% JavaScript 46.82%
explain workerize explain-workerize

explain-workerize's Introduction

workerize

explain

在Web Worker中运行一个模块。

作者是 Preact- 小型React库的作者

github source


尝试 workderize, 在 调试console

let worker = window.workerize(`
	export function add(a, b) {
		// block for half a second to demonstrate asynchronicity
		let start = Date.now();
		while (Date.now()-start < 500);
		return a + b;
	}
`);

(async () => {
	console.log('3 + 9 = ', await worker.add(3, 9));
	console.log('1 + 2 = ', await worker.add(1, 2));
})();

在本目录,启动http

python

python -m SimpleHTTPServer

javascript

npx http-server .

workerize

因为只有一个文件

index.js


按照给出的例子,和作者描述

  • 将一个小型的专门构建的RPC实现捆绑到您的应用程序中

  • 如果导出的模块方法已经是异步的,那么签名是不变的

  • 支持同步和异步工作者功能

  • 与异步/等待美妙地工作



workerize

代码 22-34

export default function workerize(code) {
	let exports = {};
	let exportsObjName = `__EXPORTS_${Math.random().toString().substring(2)}__`; // 随机 id
	if (typeof code==='function') code = `(${toCode(code)})(${exportsObjName})`; 
	// 如果直接输出函数,从我们例子来看,先不管
	code = toCjs(code, exportsObjName, exports);
	// 那么现在先去看 toCjs <-----  1

	code += `\n(${toCode(setup)})(self, ${exportsObjName}, {})`;
	// <--- 2 把 setup 函数加进来
	let blob = new Blob([code], {
			type: 'application/javascript'
		}),
		url = URL.createObjectURL(blob), // 变成了可以被加载的 Url
		worker = new Worker(url),
		counter = 0,
		callbacks = {};

函数变文本

改造文本,记录函数

  • code += setup

到这一步JsBin > code += setip

Blob 对象表示不可变的类似文件对象的原始数据。jsbin 例子�

Worker() 构造函数创建一个 Worker 对象,该对象执行指定的URL脚本。这个脚本必须遵守 同源策略 。


worker-�管理

代码 35-44

	worker.kill = signal => {
		worker.postMessage({ type: 'KILL', signal });
		setTimeout(worker.terminate);
	}; // 关闭
	let term = worker.terminate;
	worker.terminate = () => {
		URL.revokeObjectURL(url); // 丢 url
		term(); // 触发本身结束命令
    }; // 
    worker.rpcMethods = {};

开始运作

代码 84-95

	setup(worker, worker.rpcMethods, callbacks); // <---- 1
	worker.call = (method, params) => new Promise( (resolve, reject) => {
		let id = `rpc${++counter}`;
		callbacks[id] = { method, resolve, reject };
		worker.postMessage({ type: 'RPC', id, method, params });
	});
	// exports 通过 toCjs 函数 获取到了 函数名
	// exports = {
	// 	'add' : true
	// }
	for (let i in exports) {
		// i == 'add'
		if (exports.hasOwnProperty(i) && !(i in worker)) {
			worker[i] = (...args) => worker.call(i, args);
		}
	}
	return worker;

还记得 我们例子的使用 worker.add

// usage
(async () => {
	console.log('3 + 9 = ', await worker.add(3, 9)); // args 3,9
	console.log('1 + 2 = ', await worker.add(1, 2)); // args 1,2
})();
// index.js
worker[i] = (...args) => worker.call(i, args); // args=
  • worker.call

使用函数,通知 worker 内部,

	worker.call = (method, params) => new Promise( (resolve, reject) => { // 变成了 异步/Promise
		let id = `rpc${++counter}`; // 创建 唯一id
		callbacks[id] = { method, resolve, reject }; // 放入本地缓存函数集
		worker.postMessage({ type: 'RPC', id, method, params }); // 第一次触发 onmessage 事件
		// type 自定义类型, id 唯一标签, method 函数, params 变量
	});

setup

代码 45-83

ctx == worker , rpcMethods == {}, callbacks == {}

	function setup(ctx, rpcMethods, callbacks) {
		/*
		ctx.expose = (methods, replace) => {
			if (typeof methods==='string') {
				rpcMethods[methods] = replace;
			}
			else {
				if (replace===true) rpcMethods = {};
				Object.assign(rpcMethods, methods);
			}
		};
		*/
		ctx.addEventListener('message', ({ data }) => {
			if (data.type==='RPC') {
				let id = data.id;
				if (id!=null) { 
					if (data.method) { 
						let method = rpcMethods[data.method]; // 本地缓存-rpcMethods-中找出 method
						if (method==null) {
							ctx.postMessage({ type: 'RPC', id, error: 'NO_SUCH_METHOD' }); 
						}
						else {
							Promise.resolve()
								.then( () => method.apply(null, data.params) )
								.then( result => { ctx.postMessage({ type: 'RPC', id, result }); }) 
								// 触发ctx.onmessage 事件 
								// data == { type: 'RPC', id, result }); } 
								.catch( error => { ctx.postMessage({ type: 'RPC', id, error }); }); 
								// 触发ctx.onmessage 事件 
								// data == { type: 'RPC', id, error }); } 
						}
					}
					else {
						let callback = callbacks[id];  // 本地缓存-函数集-id
						if (callback==null) throw Error(`Unknown callback ${id}`);
						delete callbacks[id];
						if (data.error) callback.reject(Error(data.error));
						else callback.resolve(data.result);
					}
				}
			}
		});
	}
  • addEventListener('message', //...)

Worker - 属性

onmessage 一个事件监听函数,每当拥有 message 属性的 MessageEvent 从 worker 中冒泡出来时就会执行该函数。

事件的 data 属性存有消息内容。

  • data.type === RPC

  • ctx.postMessage

向 worker 的内部作用域内传递消息。触发 ctx.onmessage 事件

因为 ctx.postMessageonmessage 触发事件内部,所以第一次触发,不会来自内部



工具函数

toCode

function toCode(func) {
	return Function.prototype.toString.call(func);
}
  1. 定义
function add(){
}
  1. 运行

Function.prototype.toString.call(add) // 变成 String

  1. 结果

"function add(){}"


toCjs

  • code : String
	// String
	export function add(a, b) {
		// block for half a second to demonstrate asynchronicity
		let start = Date.now();
		while (Date.now()-start < 500);
		return a + b;
	}
  • exportsObjName : String

__EXPORTS_${Math.random().toString().substring(2)}__

"EXPORTS_4706166142920267"

  • exports : Object {}
function toCjs(code, exportsObjName, exports) {
	exportsObjName = exportsObjName || 'exports';
	exports = exports || {};
	code = code.replace(/^(\s*)export\s+default\s+/m, (s, before) => {
		// export default function
		exports.default = true; // 具有默认导出函数
		// before == ''
		return `${before}${exportsObjName}.default = `;
		// code 将变成 __EXPORTS_4706166142920267__.default = function ...
		// 但是 这个例子 没有 default
	});
	code = code.replace(/^(\s*)export\s+(function|const|let|var)(\s+)([a-zA-Z$_][a-zA-Z0-9$_]*)/m, (s, before, type, ws, name) => {
		exports[name] = true;
		return `${before}${exportsObjName}.${name} = ${type}${ws}${name}`;
	});
	// code ==
	// `__EXPORTS_4706166142920267__.add = function add(a, b) {
	// 	// block for half a second to demonstrate asynchronicity
	// 	let start = Date.now();
	// 	while (Date.now()-start < 500);
	// 	return a + b;
	// }`

	return `var ${exportsObjName} = {};\n${code}\n${exportsObjName};`;

	// return 
	// var __EXPORTS_4706166142920267__ = {};
	// `__EXPORTS_4706166142920267__.add = function add(a, b) {
	// 	// block for half a second to demonstrate asynchronicity
	// 	let start = Date.now();
	// 	while (Date.now()-start < 500);
	// 	return a + b;
	// }`
	// __EXPORTS_4706166142920267__
}

本例子的-JsBin-

code.replace

replace() 方法返回一个由替换值替换一些或所有匹配的模式后的新字符串。

模式可以是一个字符串或者一个正则表达式, 替换值可以是一个字符串或者一个每次匹配都要调用的函数。

mdn文档

第一个�正则式说明

code.replace(/^(\s*)export\s+default\s+/m,)

^

开头

\s

匹配一个空白符,包括空格、制表符、换页符、换行符和其他 Unicode 空格。

( ) (\s)

匹配 \s 并且捕获匹配项。 这被称为捕获括号(capturing parentheses)。

*

匹配前面的模式 \s 0 或多次。

export

直白匹配 export 文本

+

匹配前面的模式 \s 1 或多次。

/m

多行; 将开始和结束字符(^和$)视为在多行上工作(也就是,分别匹配每一行的开始和结束(由 \n 或 \r 分割),而不只是只匹配整个输入字符串的最开始和最末尾处。


第二个正则式说明

code.replace(/^(\s*)export\s+(function|const|let|var)(\s+)([a-zA-Z$_][a-zA-Z0-9$_]*)

(function|const|)

匹配 functionconst

[a-zA-Z$_]

一个字符集合,也叫字符组。匹配集合中的任意一个字符。你可以使用连字符'-'指定一个范围。

比如这个就是 小写的 a到z 大写 的A到Z $_

return

var ${exportsObjName} = {};\n${code}\n${exportsObjName};


Blob

mdn文档

worker

mdn文档

整体概括

我们回顾一下,作者提到这个库的特点

  • 将一个小型的专门构建的RPC实现捆绑到您的应用程序中

  • 如果导出的模块方法已经是 async ,那么签名是不变的

  • 支持 syncasync 工作者功能

  • async/await美妙地工作


  1. RPC 远程过程调用协议 ?

  2. 我看到的签名,应该是 唯一 id 或者是 exportsObjName 这个随机值

    • worker[i] = (...args) => worker.call(i, args); 的使用

    • worker.call = (method, params) => new Promise( 的定义,必定是异步

  3. 上面也说了,Promise ,自然与async/await 美妙结合


整体来说,作者活用了 Blob worker 两个api

真的厉害:)


其他

作者使用的 构建工具

是 零配置的,power by rollup 的自己写的工具。

explain-workerize's People

Contributors

chinanf-boy avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.