Implement Basic Worker API - Interface
A Worker has two parts –the worker, and the interface– each goes in a pod within /app/workers.
interface.js is a subclass of Skyrocket.Interface and allows a user to define the exposed API for the worker
import Skyrocket from "skyrocket";
const {
prop,
event,
func,
sendEvent
} = Skyrocket;
export default Skyrocket.Interface.extend({
fetch: func(),
find: func(),
foo: prop(),
bar: prop(),
message: event(),
notify: sendEvent()
});
This interface is instantiated when resolved for the first time and itself instantiates and manages Worker.
{
stack: Ember.inject.worker('stack')
}
The instantiated interface can be interacted with much like a normal Ember.Object, except that all properties accessed and functions invoked return promises.
this.stack.find('foo'); // returns a promise
Properties must be accessed via get/set, and return a promise. This promise behavior should be debated, it is possible to create a syncing IO style binding for the property.
this.stack.get('bar'); // also returns a promise
Events can be listened to via on.
this.stack.on('message', this, myFunction);
this.stack.off('message', this, myFunction);
Whether prop is exposed as a computed property or as a property-like method returning a promise with the value has the same issue with data synchronization. Since a WebWorker is it's own thread, it is possible a value changes while you are using it.
Because of this, I recommend having get('myProp') behave like computed and not return a promise, along with clear documentation and examples pointing out that your trust level for property accuracy should be lower.
Interface should also have a sendEvent method for sending events to the worker.
sendEvent func and event should all expect variable argument lengths and send their payloads through in the following manner.
For an event
var args = Array.prototype.slice.call(arguments);
postMessage(JSON.stringify({
event: {
name: ‘message’,
args: args
}
}));
For a function invocation
var args = Array.prototype.slice.call(arguments);
postMessage(JSON.stringify({
func: {
name: ‘foo’,
args: args
}
}));
An interface needs to know it's name.
e.g. <worker-name> from app/workers/<worker-name>/interface.js needs to somehow be exposed as interfaceName on the interface. The interface can instantiate it's worker by calling worker = new Worker('assets/workers/' + interfaceName + '.js');;
Should be noted in that last example, using postMessage(JSON.stringify( is a detail that will be abstracted away. Interface should have a __pipeData() method that determines what transfer mechanism to use based on the feature detection done in #5
Also should be noted, we could allow flexible eventing (undefined events can go into or come out of the worker). The reason I've setup the Interface API to not allow this, is that by explicitly defining the expected and allowed events, we're able to provide a much better warning mechanism along the lines of "You subscribed to the eventmessageon the Interfacechat-stream, but no such event exists and your handler will never trigger."
-
eventsdon't have returns -
funcreturns the result of the promise returned by your method (this means that all worker methods exposed via the interface must return a promise) -
propreturns the current state of the property. If the property is set, all other worker subscribers will be notified when it changes.