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,