Skip to content
This repository was archived by the owner on Sep 23, 2022. It is now read-only.

Commit 937dc02

Browse files
committed
Enhance WorkerProxy, part of #32
1 parent 68ba909 commit 937dc02

File tree

2 files changed

+84
-26
lines changed

2 files changed

+84
-26
lines changed

scripts/app/main.js

+25-17
Original file line numberDiff line numberDiff line change
@@ -143,42 +143,50 @@ FlareTail.app.WorkerProxy = class WorkerProxy {
143143
/**
144144
* Get a WorkerProxy instance.
145145
* @constructor
146-
* @argument {String} class_name - Name of the target class on the worker.
146+
* @argument {String} object_path - Path to the target object on the worker, like 'BzDeck.collections.bugs'.
147147
* @argument {SharedWorker} [worker=undefined] If specified, use the shared worker instead of the service worker.
148148
* @return {Proxy} proxy - The catch-all magic mechanism. Functions on this proxy will return a Promise.
149149
*/
150-
constructor (class_name, worker = undefined) {
150+
constructor (object_path, worker = undefined) {
151151
return new Proxy({}, {
152152
get: (obj, func_name) => new Proxy(() => {}, {
153-
apply: (_obj, _this, args) => new Promise(resolve => {
153+
apply: (_obj, _this, args) => new Promise((resolve, reject) => {
154+
let id = FlareTail.helpers.misc.uuidgen(),
155+
func_path = [object_path, func_name].join('.'),
156+
message = { type: 'WorkerProxyRequest', id, func_path, args };
157+
154158
let listener = event => {
155-
let [ service, type, detail ] = event.data;
159+
if (event.data.type !== 'WorkerProxyResponse' || event.data.id !== id) {
160+
return;
161+
}
156162

157-
if (service === class_name && type === func_name) {
158-
if (FlareTail.debug) {
159-
console.info('[WorkerProxy] received message:', class_name, func_name, detail);
160-
}
163+
if (worker) {
164+
worker.port.removeEventListener('message', listener);
165+
} else {
166+
navigator.serviceWorker.removeEventListener('message', listener);
167+
}
161168

162-
if (worker) {
163-
worker.port.removeEventListener('message', listener);
164-
} else {
165-
navigator.serviceWorker.removeEventListener('message', listener);
166-
}
169+
if (FlareTail.debug) {
170+
console.info('[WorkerProxyResponse]', func_path, event.data.result);
171+
}
167172

168-
resolve(detail);
173+
if (event.data.error) {
174+
reject(new Error(event.data.error));
175+
} else {
176+
resolve(event.data.result);
169177
}
170178
};
171179

172180
if (FlareTail.debug) {
173-
console.info('[WorkerProxy] sent message:', class_name, func_name, args);
181+
console.info('[WorkerProxyRequest]', func_path, args);
174182
}
175183

176184
if (worker) {
177185
worker.port.addEventListener('message', listener);
178-
worker.port.postMessage([class_name, func_name, args]);
186+
worker.port.postMessage(message);
179187
} else {
180188
navigator.serviceWorker.addEventListener('message', listener);
181-
navigator.serviceWorker.ready.then(reg => reg.active.postMessage([class_name, func_name, args]));
189+
navigator.serviceWorker.ready.then(reg => reg.active.postMessage(message));
182190
}
183191
}),
184192
}),

scripts/app/worker.js

+59-9
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,50 @@ FlareTail.app.AbstractWorker = class AbstractWorker {
1515
constructor (name) {
1616
this.name = name;
1717

18+
self.addEventListener('connect', event => this.onconnect(event));
19+
1820
return ({
1921
datasources: {},
2022
collections: {},
2123
models: {},
2224
handlers: {},
2325
});
2426
}
27+
28+
/**
29+
* Called whenever a MessagePort connection is opened. Add support for WorkerProxy.
30+
* @argument {MessageEvent} event - A connect event.
31+
* @return {undefined}
32+
*/
33+
onconnect (event) {
34+
let port = event.ports[0];
35+
36+
port.addEventListener('message', event => {
37+
let { type, id, func_path, args } = event.data;
38+
39+
if (type === 'WorkerProxyRequest') {
40+
let obj = self; // WorkerGlobalScope
41+
42+
// Extract the target object from func_path like 'BzDeck.collections.bugs.get'
43+
for (let name of func_path.split('.')) {
44+
obj = obj[name];
45+
46+
if (!obj) {
47+
break; // Object/Function not found
48+
}
49+
}
50+
51+
// Return the results or error
52+
if (typeof obj === 'function') {
53+
obj(...args).then(result => port.postMessage({ type: 'WorkerProxyResponse', id, result }));
54+
} else {
55+
port.postMessage({ type: 'WorkerProxyResponse', id, error: 'Function Not Found' });
56+
}
57+
}
58+
});
59+
60+
port.start();
61+
}
2562
}
2663

2764
/**
@@ -107,7 +144,7 @@ FlareTail.app.Model = class Model extends FlareTail.app.Base {
107144
* @argument {undefined}
108145
* @return {Proxy} this - Proxified `this` object.
109146
*/
110-
proxy () {
147+
get proxy () {
111148
return new Proxy(this, {
112149
get: (obj, prop) => prop in this ? this[prop] : this.data[prop],
113150
set: (obj, prop, value) => {
@@ -118,6 +155,17 @@ FlareTail.app.Model = class Model extends FlareTail.app.Base {
118155
});
119156
}
120157

158+
/**
159+
* Get a transferable, clone-safe data object that contains the original data and custom attributes.
160+
* @argument {undefined}
161+
* @argument {Object} data
162+
*/
163+
get copy () {
164+
let obj = JSON.parse(JSON.stringify(this)); // Remove functions
165+
delete obj.data; // Remove data
166+
return Object.assign(obj, this.data); // Add data again
167+
}
168+
121169
/**
122170
* Cache data as a new Proxy, so the object is automatically saved when a property is modifled.
123171
* @argument {Object} data - Raw data object.
@@ -152,7 +200,7 @@ FlareTail.app.Model = class Model extends FlareTail.app.Base {
152200
console.info('Data saved:', this.constructor.name, this.data);
153201
}
154202

155-
return Promise.resolve(this.proxy());
203+
return Promise.resolve(this.proxy);
156204
});
157205
}
158206
}
@@ -224,12 +272,12 @@ FlareTail.app.Collection = class Collection extends FlareTail.app.Base {
224272
* Get an item by a specific key.
225273
* @argument {(Number|String)} key - Key of the item.
226274
* @argument {Object} [fallback_value] - If an item is not found, create a new model object with this value.
227-
* @return {Promise.<(Proxy|undefined)>} item - Promise to be resolved in a model instance.
275+
* @return {Promise.<Object>} item - Promise to be resolved in a model instance's transferable copy.
228276
*/
229277
get (key, fallback_value = undefined) {
230278
return this.has(key).then(has => {
231279
if (has) {
232-
return this.map.get(key);
280+
return this.map.get(key).copy;
233281
}
234282

235283
if (fallback_value) {
@@ -242,20 +290,22 @@ FlareTail.app.Collection = class Collection extends FlareTail.app.Base {
242290

243291
/**
244292
* Get items by specific keys.
245-
* @argument {(Array|Set).<(String|Number)>} keys - Key list.
246-
* @return {Promise.<Map.<(String|Number), Proxy>>} items - Promise to be resolved in model instances.
293+
* @argument {(Array|Set|Iterator).<(String|Number)>} keys - Key list.
294+
* @return {Promise.<Map.<(String|Number), Object>>} items - Promise to be resolved in a Map of model instances'
295+
* transferable copies.
247296
*/
248297
get_some (keys) {
249-
return Promise.resolve(new Map([...keys].map(key => [key, this.map.get(key)])));
298+
return Promise.resolve(new Map([...keys].map(key => [key, this.map.get(key).copy])));
250299
}
251300

252301
/**
253302
* Get all items locally-stored in IndexedDB.
254303
* @argument {undefined}
255-
* @return {Promise.<Map.<(String|Number), Proxy>>} items - Promise to be resolved in model instances.
304+
* @return {Promise.<Map.<(String|Number), Object>>} items - Promise to be resolved in a Map of model instances'
305+
* transferable copies.
256306
*/
257307
get_all () {
258-
return Promise.resolve(this.map);
308+
return this.get_some(this.map.keys());
259309
}
260310

261311
/**

0 commit comments

Comments
 (0)