-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtask.ts
171 lines (152 loc) · 5.54 KB
/
task.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
/**
* Реализация фоновых заданий через web worker
* (для удобства использования web worker без внешнего файла скрипта)
* BackgroundTaskFunction - сигнатура выполняемого метода в фоновом потоке,
* ITaskFunctionArg - интерфейс аргумента выполняемого метода
* ITaskFunctionArg.data - данные, передаваемые через ф-ю Run(data?:any), которая запускает задание
*
* Пример:
*
* const task = new BackgroundTask((arg) => {
const a = arg.data;
arg.completed(a*1000);
});
task.onComplete = (r) => console.log('result:' + r); //1159000
task.Run(1159);
*
*/
export class BackgroundTask {
private execFunc: string;
constructor(f: BackgroundTaskFunction) {
if (!f)
throw new Error("[.ctor BackgroundTask] Передан пустой объект в конструктор!");
this.execFunc = (<any>f).toString();
}
onProgressChanged: (step:number) => void;
onComplete: <T>(result:T) => void;
onError: (error) => void;
/**Запуск worker'а с передачей данных в св-во data аргумента ITaskFunctionArg */
Run(data?:any) {
if (!this.execFunc)
return;
const runEvent: ITaskMessage<any> = {
data: [this.execFunc, data],
type: TaskEventType.exec
};
this.worker && this.worker.postMessage(runEvent);
}
/**Загружает внешние библиотеки в контекст worker'а
* Пример:
* t.LoadLibrary('http://localhost:8080/mylibrary.js');
*/
LoadLibrary(...libs:string[]) {
if (!libs)
return;
const runEvent: ITaskMessage<any> = {
data: libs,
type: TaskEventType.library
};
this.worker && this.worker.postMessage(runEvent);
}
private get worker() {
const w = BackgroundTask.workerInstance;
w.onerror = (err) => this.onError && this.onError(err);
w.onmessage = (e:any) => {
const message: ITaskMessage<any> = e.data;
switch (message.type) {
case TaskEventType.complete:
this.onComplete && this.onComplete(message.data);
break;
case TaskEventType.reportProgress:
this.onProgressChanged && this.onProgressChanged(message.data);
break;
case TaskEventType.error:
this.onError && this.onError(message.data);
break;
default:
break;
}
};
return w;
}
private static _worker:Worker;
private static get workerInstance() {
if (!BackgroundTask._worker)
BackgroundTask._worker = BackgroundTask.buildWorker();
return BackgroundTask._worker;
}
private static buildWorker():Worker {
const sandbox = () => {
const _pushMessage = <T>(data:ITaskMessage<T>) => {
(postMessage as any)(data);
}
const _reportProgress = (step) => {
_pushMessage<number>({
type: 1,
data: step
});
}
const _onComplete = (result) => {
_pushMessage<any>({
type: 2,
data: result
});
}
const _errorReport = (err) => {
_pushMessage<any>({
type: 3,
data: err
});
}
const _sandboxExecute = (arg:any[]) => {
const f = new Function("return " + arg[0])();
try {
const param: ITaskFunctionArg<any> = {
data: arg[1],
reportProgress: _reportProgress,
errorReport: _errorReport,
completed: _onComplete
};
f(param);
} catch (e) {
_errorReport(e);
}
}
onmessage = (e) => {
const message: ITaskMessage<any> = e.data;
switch (message.type) {
case 5:
importScripts(message.data);
break;
case 4:
_sandboxExecute(message.data);
break;
default:
break;
}
};
}
let code = sandbox.toString();
code = code.substring(code.indexOf("{")+1, code.lastIndexOf("}"));
const blob = new Blob([code], {type: "application/javascript"});
return new Worker(URL.createObjectURL(blob));
}
}
export type BackgroundTaskFunction = (arg:ITaskFunctionArg<any>) => any;
export enum TaskEventType {
reportProgress = 1,
complete = 2,
error = 3,
exec = 4,
library = 5
}
export interface ITaskMessage <T> {
type: TaskEventType,
data: T
}
export interface ITaskFunctionArg <T> {
reportProgress: (step:number) => void;
errorReport: (err) => void;
completed: (data: any) => void;
data: T;
}