forked from fanruan/fineui
Browse Source
Merge in VISUAL/fineui from ~TELLER/fineui:KERNEL-11169 to master * commit 'b78e9a4795996127f55440e115919e96b83a05e7': feat: 添加demo chore: eslint修复 refactor: 调整下数据结构 fix: worker环境秀谷 feat: 输出workers refactor: 修改下export feat: 主次线程入口abstract类 feat: action基础类 feat: 新增各线程controller refactor: 改名更换位置 refactor: 移动个位置 refactor: 改名 chore: 更新版本号 refactor: 统一下名字 refactor: 改个名字 feat: 框架三大件写完 chore: 更新依赖 feat: 新增promise判断方法es6
Teller
2 years ago
19 changed files with 830 additions and 4 deletions
@ -0,0 +1,15 @@ |
|||||||
|
<!DOCTYPE html> |
||||||
|
<html lang="en"> |
||||||
|
<head> |
||||||
|
<meta charset="UTF-8"> |
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge"> |
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||||
|
<title>Document</title> |
||||||
|
<link rel="stylesheet" type="text/css" href="http://fanruan.design/fineui/2.0/fineui.min.css" /> |
||||||
|
<script src="http://fanruan.design/fineui/2.0/fineui.min.js"></script> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<div id="wrapper"></div> |
||||||
|
<script src="./worker_new/index.js"></script> |
||||||
|
</body> |
||||||
|
</html> |
@ -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(); |
||||||
|
} |
||||||
|
} |
||||||
|
] |
||||||
|
}); |
@ -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(); |
@ -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() {} |
||||||
|
} |
@ -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<T = any>(actionType: string, payload: any = '', timeout?: number): Promise<T> { |
||||||
|
// 有 Channel 实例才能进行通信, 此时还没有实例化是浏览器不支持创建 worker
|
||||||
|
if (this.channel) { |
||||||
|
return this.channel.requestPromise<T>(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<any> { |
||||||
|
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]; |
||||||
|
} |
||||||
|
} |
@ -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; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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); |
||||||
|
} |
||||||
|
} |
@ -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<IWorkerMessage>} 等待响应的 Promise |
||||||
|
*/ |
||||||
|
public requestPromise<T>(actionType: string, payload: any, timeout = COMMUNICATION_TIMEOUT): Promise<T> { |
||||||
|
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]; |
||||||
|
} |
||||||
|
} |
@ -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<any>; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Worker创建配置 |
||||||
|
*/ |
||||||
|
export interface IWorkerOptions { |
||||||
|
|
||||||
|
/** |
||||||
|
* worker 资源 url |
||||||
|
*/ |
||||||
|
workerUrl: string; |
||||||
|
|
||||||
|
/** |
||||||
|
* worker 实例名称 |
||||||
|
*/ |
||||||
|
workerName: string; |
||||||
|
} |
@ -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<T extends typeof WorkerBaseAction>(Action: T): InstanceType<T> { |
||||||
|
return (new Action(this.controller, this)) as InstanceType<T>; |
||||||
|
} |
||||||
|
} |
@ -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<T extends typeof WorkerBaseAction>(Action: T): InstanceType<T> { |
||||||
|
return (new Action(this.controller, this)) as InstanceType<T>; |
||||||
|
} |
||||||
|
} |
@ -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, |
||||||
|
}; |
Loading…
Reference in new issue