diff --git a/.eslintrc b/.eslintrc
index c435e6b66..cb2d8acd2 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -26,7 +26,7 @@
},
"plugins": ["@typescript-eslint/eslint-plugin"],
"overrides": [{
- "files": ["src/*.js","src/**/*.js", "demo/*.js", "demo/**/*.js", "i18n/**/*.js", "i18n/*.js", "test/**/*.js", "test/*.js"],
+ "files": ["src/*.js","src/**/*.js", "demo/*.js", "demo/**/*.js", "i18n/**/*.js", "i18n/*.js", "test/**/*.js", "test/*.js", "examples/*.js", "examples/**/*.js"],
"extends": "plugin:@fui/es5",
"rules": {
"no-param-reassign": "off",
diff --git a/examples/worker_new.html b/examples/worker_new.html
new file mode 100644
index 000000000..53e83c4ee
--- /dev/null
+++ b/examples/worker_new.html
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+ Document
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/worker_new/index.js b/examples/worker_new/index.js
new file mode 100644
index 000000000..67646a1ec
--- /dev/null
+++ b/examples/worker_new/index.js
@@ -0,0 +1,105 @@
+document.cookie = "test=demo";
+
+// worker获取主线程资源
+var CookieAction = BI.inherit(BI.Workers.WorkerBaseAction, {
+ addActionHandler: function() {
+ this.controller.addActionHandler("Cookie", this.getCookie.bind(this));
+ },
+
+ getCookie: function() {
+ return document.cookie;
+ }
+});
+
+// 调用worker计算
+var FibonacciAction = BI.inherit(BI.Workers.WorkerBaseAction, {
+ addActionHandler: function() {},
+
+ getResult: function(times) {
+ return this.controller.requestPromise("Fibonacci", { times: times })
+ .then(function(v) {
+ console.log(v);
+ });
+ }
+});
+
+// 主线程与worker多次交互
+const HeartBeatCheckAction = BI.inherit(BI.Workers.WorkerBaseAction, {
+ addActionHandler: function() {
+ this.controller.addActionHandler("HeartBeatChecked", this.recieveHeartBeatChecked.bind(this));
+ },
+
+ recieveHeartBeatChecked: function (payload) {
+ console.log("heartbeat: " + payload.time);
+ },
+
+ startHeatBeatCheck: function() {
+ return this.controller.request("HeartBeatCheckStart");
+ },
+
+ stopHeatBeatCheck: function() {
+ return this.controller.request("HeartBeatCheckStop");
+ }
+});
+
+var WorkerThreadWorker = BI.inherit(BI.Workers.MainThreadWorker, {
+ initActions: function() {
+ this.cookieAction = this.createAction(CookieAction);
+
+ this.fibonacciAction = this.createAction(FibonacciAction);
+
+ this.heartBeatCheckAction = this.createAction(HeartBeatCheckAction);
+ },
+
+ calculateFibonacci: function(times) {
+ this.fibonacciAction.getResult(times);
+ },
+
+ startHeatBeatCheck: function() {
+ this.heartBeatCheckAction.startHeatBeatCheck();
+ },
+
+ stopHeatBeatCheck: function() {
+ this.heartBeatCheckAction.stopHeatBeatCheck();
+ }
+});
+
+var mainThreadWorker = new WorkerThreadWorker({
+ workerUrl: "./worker_new/worker.js",
+ workerName: "demo"
+});
+
+BI.createWidget({
+ type: "bi.vertical",
+ element: "#wrapper",
+ vgap: 10,
+ hgap: 10,
+ items: [
+ {
+ type: "bi.button",
+ text: "点击计算斐波那契数列第40项",
+ width: 200,
+ handler: function() {
+ console.log("click");
+
+ mainThreadWorker.calculateFibonacci(40);
+ }
+ },
+ {
+ type: "bi.button",
+ text: "开始心跳",
+ width: 200,
+ handler: function() {
+ mainThreadWorker.startHeatBeatCheck();
+ }
+ },
+ {
+ type: "bi.button",
+ text: "停止心跳",
+ width: 200,
+ handler: function() {
+ mainThreadWorker.stopHeatBeatCheck();
+ }
+ }
+ ]
+});
diff --git a/examples/worker_new/worker.js b/examples/worker_new/worker.js
new file mode 100644
index 000000000..f30856b21
--- /dev/null
+++ b/examples/worker_new/worker.js
@@ -0,0 +1,80 @@
+self.importScripts("https://fanruan.design/fineui/fineui_without_jquery_polyfill.js");
+
+var CookieAction = BI.inherit(BI.Workers.WorkerBaseAction, {
+ addActionHandler: function() {},
+
+ getCookie: function() {
+ return this.controller.requestPromise("Cookie");
+ }
+});
+
+function fibonacci(n) {
+ if (n === 1 || n === 2) {
+ return 1;
+ }
+
+ return fibonacci(n - 2) + fibonacci(n - 1);
+}
+
+var FibonacciAction = BI.inherit(BI.Workers.WorkerBaseAction, {
+ addActionHandler: function() {
+ this.controller.addActionHandler("Fibonacci", this.getResult.bind(this));
+ },
+
+ getResult: function(payload) {
+ return fibonacci(payload.times);
+ }
+});
+
+const HeartBeatCheckAction = BI.inherit(BI.Workers.WorkerBaseAction, {
+ addActionHandler: function() {
+ this.controller.addActionHandler("HeartBeatCheckStart", this.startHeatBeatCheck.bind(this));
+ this.controller.addActionHandler("HeartBeatCheckStop", this.stopHeatBeatCheck.bind(this));
+ },
+
+ startHeatBeatCheck: function() {
+ var self = this;
+
+ if (!this.timer) {
+ console.log("heart beat check started");
+
+ this.timer = setInterval(function() {
+ // 模拟请求
+ setTimeout(function() {
+ self.controller.request("HeartBeatChecked", {
+ time: new Date()
+ });
+ }, 50);
+ }, 5 * 1000);
+ } else {
+ console.log("heart beat has already started!");
+ }
+ },
+
+ stopHeatBeatCheck: function() {
+ console.log("heart beat check stopped");
+
+ clearInterval(this.timer);
+ }
+});
+
+var WorkerThreadWorker = BI.inherit(BI.Workers.WorkerThreadWorker, {
+ initActions: function() {
+ this.cookieAction = this.createAction(CookieAction);
+
+ this.fibonacciAction = this.createAction(FibonacciAction);
+
+ this.heartBeatCheckAction = this.createAction(HeartBeatCheckAction);
+ },
+
+ fetchCookie: function() {
+ return this.cookieAction.getCookie()
+ .then(function (v) {
+ console.log(v);
+ });
+ }
+});
+
+var workerThreadWorker = new WorkerThreadWorker();
+
+workerThreadWorker.fetchCookie();
diff --git a/package.json b/package.json
index 2b1a4e252..a7ebe7bb4 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "fineui",
- "version": "2.0.20220627090545",
+ "version": "2.0.20220701111336",
"description": "fineui",
"main": "dist/fineui_without_conflict.min.js",
"types": "dist/lib/index.d.ts",
@@ -8,7 +8,7 @@
"@babel/core": "^7.17.4",
"@babel/polyfill": "7.6.0",
"@fui/babel-preset-fineui": "^2.0.0",
- "@fui/eslint-plugin": "1.0.11",
+ "@fui/eslint-plugin": "1.0.15",
"@types/node": "15.6.1",
"autoprefixer": "9.6.1",
"babel-loader": "8.0.6",
@@ -47,7 +47,7 @@
"source-map-loader": "0.2.4",
"style-loader": "0.23.1",
"terser-webpack-plugin": "4.2.3",
- "typescript": "3.5.2",
+ "typescript": "3.9.2",
"webpack": "4.35.2",
"webpack-cli": "3.3.5",
"webpack-dev-server": "3.7.2",
diff --git a/src/base/single/editor/editor.multifile.js b/src/base/single/editor/editor.multifile.js
index 7aec220f0..ae0d5e0a9 100644
--- a/src/base/single/editor/editor.multifile.js
+++ b/src/base/single/editor/editor.multifile.js
@@ -71,6 +71,10 @@ BI.MultifileEditor = BI.inherit(BI.Widget, {
this.file.reset();
},
+ setUrl: function (v) {
+ this.file.setUrl(v);
+ },
+
setMaxFileLength: function (v) {
this.file.setMaxFileLength(v);
},
diff --git a/src/base/single/input/file.js b/src/base/single/input/file.js
index b86963f41..ff211e6f1 100644
--- a/src/base/single/input/file.js
+++ b/src/base/single/input/file.js
@@ -655,6 +655,13 @@
});
},
+ setUrl: function(v) {
+ this.options.url = v;
+ if (this.wrap) {
+ this.wrap.url = v;
+ }
+ },
+
setMaxFileLength: function (v) {
this.options.maxLength = v;
if (this.wrap) {
diff --git a/src/core/2.base.js b/src/core/2.base.js
index 4ab530629..7071eb3e1 100644
--- a/src/core/2.base.js
+++ b/src/core/2.base.js
@@ -524,6 +524,10 @@
isWindow: function (obj) {
return obj != null && obj == obj.window;
+ },
+
+ isPromise: function(obj) {
+ return !!obj && (BI.isObject(obj) || BI.isFunction(obj)) && BI.isFunction(obj.then);
}
});
diff --git a/src/core/conflict.js b/src/core/conflict.js
index e478c567d..dba9b8b6f 100644
--- a/src/core/conflict.js
+++ b/src/core/conflict.js
@@ -1,7 +1,7 @@
-if (!window.$ && !window.jQuery) {
- window.jQuery = window.$ = BI.jQuery;
+if (!_global.$ && !_global.jQuery) {
+ _global.jQuery = _global.$ = BI.jQuery;
}
-if (!window._) {
- window._ = BI._;
+if (!_global._) {
+ _global._ = BI._;
}
diff --git a/tsconfig.json b/tsconfig.json
index 8a4138e71..5630b275a 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -28,5 +28,7 @@
"types/*.d.ts",
"src/*.js",
"src/**/*.js",
+ "examples/*.js",
+ "examples/**/*.js",
]
}
\ No newline at end of file
diff --git a/types/globals.d.ts b/types/globals.d.ts
index f35336db4..0b5805aa0 100644
--- a/types/globals.d.ts
+++ b/types/globals.d.ts
@@ -11,3 +11,5 @@ declare const Fix: Obj;
declare interface String {
replaceAll(regx: string, callback: (str: string) => void): string;
}
+
+declare const _global: typeof window;
diff --git a/typescript/core/base.ts b/typescript/core/base.ts
index a30828489..1d6022a1c 100644
--- a/typescript/core/base.ts
+++ b/typescript/core/base.ts
@@ -376,6 +376,12 @@ export interface _base {
getDate: (...args: (number | string)[]) => Date;
getTime: (...args: any[]) => number;
+
+ /**
+ * 判断一个对象是不是promise
+ * @param obj 对象
+ */
+ isPromise: (obj: any) => obj is Promise;
}
type merge = {
diff --git a/typescript/core/worker/action/worker.action.ts b/typescript/core/worker/action/worker.action.ts
new file mode 100644
index 000000000..8f6920003
--- /dev/null
+++ b/typescript/core/worker/action/worker.action.ts
@@ -0,0 +1,28 @@
+import type { WorkerBaseController } from "../controller/worker.controller";
+
+/**
+ * 事务的基类
+ */
+export class WorkerBaseAction {
+ /**
+ * 通信控制器
+ */
+ protected controller: WorkerBaseController;
+
+ /**
+ * 线程上的 action 集合, 用于调用其他命名空间下的事务
+ */
+ protected threadAction: any;
+
+ public constructor(controller: WorkerBaseController, threadAction: any) {
+ this.controller = controller;
+ this.threadAction = threadAction;
+
+ this.addActionHandler();
+ }
+
+ /**
+ * 添加事务的处理器
+ */
+ protected addActionHandler() {}
+}
diff --git a/typescript/core/worker/controller/worker.controller.ts b/typescript/core/worker/controller/worker.controller.ts
new file mode 100644
index 000000000..123bba98a
--- /dev/null
+++ b/typescript/core/worker/controller/worker.controller.ts
@@ -0,0 +1,131 @@
+import type { IWorkerController, IWorkerMessage } from '../worker.core';
+import { WorkerChannel } from '../worker.channel';
+
+/**
+ * 通信控制器
+ *
+ * @class WorkerBaseController
+ */
+export class WorkerBaseController implements IWorkerController {
+ /**
+ * 原生 worker, 在子类中实例化
+ */
+ protected worker: Worker;
+
+ /**
+ * 通信 Channel, 在子类中实例化
+ */
+ protected channel: WorkerChannel;
+
+ /**
+ * 事务处理器 Map
+ */
+ protected actionHandlerMap: {
+ [propsName: string]: (payload: any) => any;
+ };
+
+ public constructor() {
+ this.actionHandlerMap = {};
+ }
+
+ /**
+ * 发送事务,不等待结果
+ *
+ * @param actionType 事务类型
+ * @param payload 负载
+ */
+ public request(actionType: string, payload: any): void {
+ if (this.channel) {
+ return this.channel.request(actionType, payload);
+ }
+
+ console.error('No channel.');
+
+ return;
+ }
+
+ /**
+ * 发送 Promise 形式的事务, 在 then 中获取响应
+ *
+ * @param actionType 事务类型
+ * @param payload 负载
+ * @param [timeout] 响应的超时; Worker 通道是可靠的, 超时后只上报, 不阻止当前请求
+ */
+ public requestPromise(actionType: string, payload: any = '', timeout?: number): Promise {
+ // 有 Channel 实例才能进行通信, 此时还没有实例化是浏览器不支持创建 worker
+ if (this.channel) {
+ return this.channel.requestPromise(actionType, payload, timeout);
+ }
+
+ // 兼容上层调用的 .then().catch()
+ return Promise.reject(new Error('No channel.'));
+ }
+
+ /**
+ * 添加事务处理器, 不允许重复添加
+ *
+ * @param actionType 事务类型
+ * @param handler 事务处理器
+ */
+ public addActionHandler(actionType: string, handler: (payload: any) => any): void {
+ if (this.hasActionHandler(actionType)) {
+ throw new Error(`Add action \`${actionType}\` handler repeat`);
+ }
+ this.actionHandlerMap[actionType] = handler;
+ }
+
+ /**
+ * 事务处理器, 提供给通信 Channel 调用
+ *
+ * @param message 会话消息
+ * @returns
+ */
+ public actionHandler(message: IWorkerMessage): Promise {
+ const { actionType, payload } = message;
+
+ if (this.hasActionHandler(actionType)) {
+ // 执行指定的事务处理器, 并返回 Promise 封装的事务结果
+ try {
+ const actionResult = this.actionHandlerMap[actionType](payload);
+
+ // 对于 Promise 形式的结果, 需要进行 Promise 错误捕获
+ if (BI.isPromise(actionResult)) {
+ return actionResult.catch(error => Promise.reject(error));
+ }
+
+ // 对数据结果, 包装为 Promise
+ return Promise.resolve(actionResult);
+ } catch (error) {
+ // 继续抛出给外层
+ return Promise.reject(error);
+ }
+ } else {
+ throw new Error(`Not Found Session Handler \`${actionType}\`.`);
+ }
+ }
+
+ /**
+ * 添加 worker onmessage 事件的回调
+ *
+ * @param {(event: any) => void} onmessage 回调函数
+ * @returns {() => void} 移除监听函数
+ */
+ public addOnmessageListener(onmessage: (event: any) => void): () => void {
+ this.worker.addEventListener('message', onmessage);
+
+ // 返回移除监听函数
+ return () => {
+ this.worker.removeEventListener('message', onmessage);
+ };
+ }
+
+ /**
+ * 判断是否有指定事务的处理器
+ *
+ * @param actionType 事务类型
+ * @returns {boolean}
+ */
+ protected hasActionHandler(actionType: string): boolean {
+ return !!this.actionHandlerMap[actionType];
+ }
+}
diff --git a/typescript/core/worker/controller/worker.main_thread.controller.ts b/typescript/core/worker/controller/worker.main_thread.controller.ts
new file mode 100644
index 000000000..bd7dcfc96
--- /dev/null
+++ b/typescript/core/worker/controller/worker.main_thread.controller.ts
@@ -0,0 +1,85 @@
+import { WorkerChannel } from "../worker.channel";
+import type { IWorkerOptions } from "../worker.core";
+import { WorkerBaseController } from "./worker.controller";
+
+export class WorkerMainThreadController extends WorkerBaseController {
+ /**
+ * 浏览器是否实现了 HTML 规范的 Worker Class
+ */
+ public static hasWorkerClass = !!_global.Worker;
+
+ /**
+ * 是否支持 new Worker, 默认为 Wroker Class 是否实现
+ */
+
+ public canNewWorker: boolean = WorkerMainThreadController.hasWorkerClass;
+
+ /**
+ * 主线程 new Worker 起始时刻
+ */
+ public timeBeforeNewWorker: number;
+
+ /**
+ * 主线程 new Worker 完毕时刻
+ */
+ public timeAfterNewWorker: number;
+
+ public constructor(options: IWorkerOptions) {
+ super();
+
+ if (!this.canNewWorker) {
+ // 都没有 Worker Class, 没法继续了
+ return;
+ }
+
+ this.newWorker(options);
+ }
+
+ /**
+ * 销毁 Worker 线程实例
+ */
+ public terminate(): void {
+ this.worker?.terminate();
+ }
+
+ protected reportActionHandlerError(actionType: string, error: any): void {
+ console.error(`Worker aciton ${actionType}:`, error);
+
+ // 主线程的报错, 在 window.onerror 中可以拿到报错堆栈, 直接抛出即可
+ throw new Error(error);
+ }
+
+ /**
+ * 创建 Worker 线程实例
+ */
+ private newWorker(options: IWorkerOptions) {
+ this.timeBeforeNewWorker = Date.now();
+
+ try {
+ // 主线程通过 new Worker() 获取 Worker 实例
+ this.worker = new Worker(options.workerUrl, {
+ name: options.workerName,
+ });
+
+ /**
+ * 监控和上报 worker 中的报错
+ * window.onerror 中也能监控到 worker.onerror( Worker 运行报错)
+ */
+ this.worker.onerror = (error): void => {
+ console.error('Worker onerror:', error);
+ };
+
+ this.timeAfterNewWorker = Date.now();
+
+ // 实例化 Channel
+ this.channel = new WorkerChannel(this.worker, {
+ actionHandler: this.actionHandler.bind(this),
+ });
+ } catch (error) {
+ console.error('Init worker fail:', error);
+
+ // 创建 worker 失败, 标识改为不支持
+ this.canNewWorker = false;
+ }
+ }
+}
diff --git a/typescript/core/worker/controller/worker.worker_thread.controller.ts b/typescript/core/worker/controller/worker.worker_thread.controller.ts
new file mode 100644
index 000000000..2f4bac6cf
--- /dev/null
+++ b/typescript/core/worker/controller/worker.worker_thread.controller.ts
@@ -0,0 +1,21 @@
+import { WorkerChannel } from "../worker.channel";
+import { WorkerBaseController } from "./worker.controller";
+
+export class WorkerThreadController extends WorkerBaseController {
+ public constructor() {
+ super();
+
+ // Worker 线程中的全局环境 self 就是 Worker 实例
+ this.worker = self as any;
+ this.channel = new WorkerChannel(this.worker, {
+ actionHandler: this.actionHandler.bind(this),
+ });
+ }
+
+ protected reportActionHandlerError(actionType: string, error: any): void {
+ console.error(`Worker aciton ${actionType}:`, error);
+
+ // 正常抛出
+ throw new Error(error);
+ }
+}
diff --git a/typescript/core/worker/worker.channel.ts b/typescript/core/worker/worker.channel.ts
new file mode 100644
index 000000000..a39124a14
--- /dev/null
+++ b/typescript/core/worker/worker.channel.ts
@@ -0,0 +1,200 @@
+import { IWorkerController, WorkerMessageType, IWorkerMessage } from './worker.core';
+
+const COMMUNICATION_TIMEOUT = 30000;
+
+/**
+ * 通信通道
+ */
+export class WorkerChannel {
+ /**
+ * Web Worker 实例
+ */
+ private worker: Worker;
+
+ /**
+ * 上层通信控制器
+ */
+ private controller: IWorkerController;
+
+ /**
+ * 会话响应器 Map
+ */
+ private sessionHandlerMap: {
+ [propsName: string]: Function;
+ };
+
+ public constructor(worker: Worker, controller: IWorkerController) {
+ this.worker = worker;
+ this.controller = controller;
+
+ this.sessionHandlerMap = {};
+
+ // 绑定 worker onmessage 事件的回调
+ this.worker.addEventListener('message', this.onmessage.bind(this));
+ }
+
+ /**
+ * 发送响应
+ *
+ * @param sessionId 会话 Id
+ * @param payload 负载
+ */
+ public response(sessionId: string, actionType: string, payload: any): void {
+ this.postMessage({
+ messageType: WorkerMessageType.REPLY,
+ actionType,
+ payload,
+ sessionId,
+ });
+ }
+
+ /**
+ * 发送请求, 不等待响应
+ *
+ * @param actionType 事务类型
+ * @param payload 负载
+ */
+ public request(actionType: string, payload: any): void {
+ const sessionId = this.generateSessionId();
+ this.postMessage({
+ messageType: WorkerMessageType.REQUEST,
+ actionType,
+ payload,
+ sessionId,
+ });
+
+ // 不等待结果, 还会收到响应, 添加个空的会话响应器
+ this.addSessionHandler(sessionId, () => {});
+ }
+
+ /**
+ * 发送请求, 并等待响应
+ *
+ * @param actionType 事务类型
+ * @param payload 负载
+ * @param timeout 响应超时
+ * @returns {Promise} 等待响应的 Promise
+ */
+ public requestPromise(actionType: string, payload: any, timeout = COMMUNICATION_TIMEOUT): Promise {
+ const sessionId = this.generateSessionId();
+ const message = {
+ messageType: WorkerMessageType.REQUEST,
+ actionType,
+ payload,
+ sessionId,
+ };
+
+ // 请求封装为一个 Promise, 等待会话响应器进行 resolve
+ const PromiseFunction = (resolve: Function, reject: Function): any => {
+ // 启动请求超时计时器
+ const timeoutHandler = setTimeout(() => {
+ clearTimeout(timeoutHandler);
+
+ reject();
+ }, timeout);
+
+ const sessionHandler: Function = (message: IWorkerMessage) => {
+ // 会话回调函数, 开始处理响应
+ this.deleteSessionHandler(message.sessionId);
+ clearTimeout(timeoutHandler);
+
+ resolve(message.payload);
+ };
+
+ this.addSessionHandler(sessionId, sessionHandler);
+
+ // 开始发送请求
+ this.postMessage(message);
+ };
+
+ return new Promise(PromiseFunction);
+ }
+
+ /**
+ * 收到会话消息的处理函数
+ *
+ * 发现是请求, 调用通信控制器的事务处理器进行处理, 获取事务结果并响应;
+ * 发现是响应,调用会话响应器
+ * @param event worker 通信事件
+ */
+ private onmessage(event: { data: IWorkerMessage }): void {
+ const { data: message } = event;
+ const { messageType, sessionId, actionType } = message;
+
+ // 接收到请求
+ if (messageType === WorkerMessageType.REQUEST) {
+ // 处理请求
+ this.controller.actionHandler(message)
+ .then(actionResult => {
+ // 响应请求
+ this.response(sessionId, actionType, actionResult);
+ });
+ }
+
+ // 接收到响应
+ if (messageType === WorkerMessageType.REPLY) {
+ // 处理响应
+ if (this.hasSessionHandler(sessionId)) {
+ this.sessionHandlerMap[sessionId](message);
+ } else {
+ throw new Error(`Session \`${sessionId}\` handler no exist`);
+ }
+ }
+ }
+
+ /**
+ * 封装的 worker 原生 postMessage 接口
+ * 支持 structured clone 和 transfer 2种通信模式
+ *
+ * @param message 会话消息
+ */
+ private postMessage(message: IWorkerMessage): void {
+ this.worker.postMessage(message);
+ }
+
+ /**
+ * 添加会话响应器
+ *
+ * @param sessionId 会话 Id
+ * @param handler 会话响应器
+ */
+ private addSessionHandler(sessionId: string, handler: Function): void {
+ if (!this.hasSessionHandler(sessionId)) {
+ this.sessionHandlerMap[sessionId] = handler;
+ } else {
+ throw new Error(`SessionId \`${sessionId}\` already exist!`);
+ }
+ }
+
+ /**
+ * 移除会话响应器
+ *
+ * @param sessionId
+ */
+ private deleteSessionHandler(sessionId: string): void {
+ if (this.hasSessionHandler(sessionId)) {
+ delete this.sessionHandlerMap[sessionId];
+ }
+ }
+
+ /**
+ * 生成每次独立会话的 Id
+ *
+ * @returns 会话 Id
+ */
+ private generateSessionId(): string {
+ const sessionId = `w_${BI.UUID()}`;
+
+ return sessionId;
+ }
+
+ /**
+ * 判断是否有指定会话的处理器
+ *
+ * @param sessionId 会话 Id
+ * @returns {boolean} 判断结果
+ */
+ private hasSessionHandler(sessionId: string): boolean {
+ return !!this.sessionHandlerMap[sessionId];
+ }
+}
diff --git a/typescript/core/worker/worker.core.ts b/typescript/core/worker/worker.core.ts
new file mode 100644
index 000000000..0c6db6186
--- /dev/null
+++ b/typescript/core/worker/worker.core.ts
@@ -0,0 +1,48 @@
+/**
+ * 会话消息类型枚举
+ */
+export const WorkerMessageType = {
+ REQUEST: 'REQUEST',
+ REPLY: 'REPLY',
+};
+
+/**
+ * 会话消息
+ */
+export interface IWorkerMessage {
+ messageType: string;
+ actionType: string;
+ sessionId: string;
+
+ /**
+ * 数据交换参数
+ */
+ payload: any;
+}
+
+/**
+ * 通信控制器需要实现的 interface
+ */
+export interface IWorkerController {
+
+ /**
+ * 事务处理器
+ */
+ actionHandler: (message: IWorkerMessage) => Promise;
+}
+
+/**
+ * Worker创建配置
+ */
+export interface IWorkerOptions {
+
+ /**
+ * worker 资源 url
+ */
+ workerUrl: string;
+
+ /**
+ * worker 实例名称
+ */
+ workerName: string;
+}
diff --git a/typescript/core/worker/worker.main_thread.ts b/typescript/core/worker/worker.main_thread.ts
new file mode 100644
index 000000000..42e0c18dd
--- /dev/null
+++ b/typescript/core/worker/worker.main_thread.ts
@@ -0,0 +1,49 @@
+import type { WorkerBaseAction } from "./action/worker.action";
+import { WorkerMainThreadController } from "./controller/worker.main_thread.controller";
+import { IWorkerOptions } from "./worker.core";
+
+/**
+ * 主线程Worker
+ */
+export abstract class MainThreadWorker {
+ /**
+ * Worker 名称
+ */
+ public name: string;
+
+ /**
+ * 主线程通信控制器
+ */
+ public controller: WorkerMainThreadController;
+
+ /**
+ * 是否已经终止掉 Worker
+ */
+ protected isTerminated = false;
+
+ public constructor(options: IWorkerOptions) {
+ this.name = options.workerName;
+ this.controller = new WorkerMainThreadController(options);
+ this.initActions();
+ }
+
+ protected abstract initActions(): void;
+
+ /**
+ * 销毁 worker 实例
+ * 子实例需要销毁action
+ */
+ public terminate(): void {
+ this.controller.terminate();
+ // 设置终止标志位
+ this.isTerminated = true;
+ }
+
+ /**
+ * 实例化action
+ * @param Action action类
+ */
+ protected createAction(Action: T): InstanceType {
+ return (new Action(this.controller, this)) as InstanceType;
+ }
+}
diff --git a/typescript/core/worker/worker.worker_thread.ts b/typescript/core/worker/worker.worker_thread.ts
new file mode 100644
index 000000000..9907955fb
--- /dev/null
+++ b/typescript/core/worker/worker.worker_thread.ts
@@ -0,0 +1,28 @@
+import type { WorkerBaseAction } from "./action/worker.action";
+import { WorkerThreadController } from "./controller/worker.worker_thread.controller";
+
+/**
+ * worker线程实例
+ */
+export abstract class WorkerThreadWorker {
+ /**
+ * Worker 线程通信控制器
+ */
+ protected controller: WorkerThreadController;
+
+ public constructor() {
+ this.controller = new WorkerThreadController();
+
+ this.initActions();
+ }
+
+ protected abstract initActions(): void;
+
+ /**
+ * 实例化action
+ * @param Action action类
+ */
+ protected createAction(Action: T): InstanceType {
+ return (new Action(this.controller, this)) as InstanceType;
+ }
+}
diff --git a/typescript/core/worker/workers.ts b/typescript/core/worker/workers.ts
new file mode 100644
index 000000000..f02447120
--- /dev/null
+++ b/typescript/core/worker/workers.ts
@@ -0,0 +1,19 @@
+import { WorkerChannel } from "./worker.channel";
+import { WorkerBaseController } from "./controller/worker.controller";
+import { WorkerMessageType } from "./worker.core";
+import { WorkerMainThreadController } from "./controller/worker.main_thread.controller";
+import { WorkerThreadController } from "./controller/worker.worker_thread.controller";
+import { WorkerBaseAction } from "./action/worker.action";
+import { MainThreadWorker } from "./worker.main_thread";
+import { WorkerThreadWorker } from "./worker.worker_thread";
+
+export const Workers = {
+ WorkerChannel,
+ WorkerBaseController,
+ WorkerMainThreadController,
+ WorkerThreadController,
+ WorkerBaseAction,
+ MainThreadWorker,
+ WorkerThreadWorker,
+ WorkerMessageType,
+};
diff --git a/typescript/index.ts b/typescript/index.ts
index fa9a08d19..c8be7c201 100644
--- a/typescript/index.ts
+++ b/typescript/index.ts
@@ -188,6 +188,7 @@ import { VerticalStickyLayout } from "./core/wrapper/layout/sticky/sticky.vertic
import { HorizontalStickyLayout } from "./core/wrapper/layout/sticky/sticky.horizontal";
import { TableLayout } from "./core/wrapper/layout/layout.table";
import './shims-tsx';
+import { Workers } from "./core/worker/workers";
export interface BI extends _func, _i18n, _base, _inject, _var, _web, _utils {
@@ -382,10 +383,12 @@ export interface BI extends _func, _i18n, _base, _inject, _var, _web, _utils {
VerticalStickyLayout: typeof VerticalStickyLayout;
HorizontalStickyLayout: typeof HorizontalStickyLayout;
TableLayout: typeof TableLayout;
+ Workers: typeof Workers;
}
export default {
Decorators: decorator,
+ Workers,
};
export {
OB,