strikejs-react
Version:
A state management framework for ReactJS applications.
202 lines (186 loc) • 4.93 kB
text/typescript
import {IMiddleware,IMiddlewareNext} from './Middleware';
import {Action} from './Action';
import {IStore} from './Store';
/**
* An action to be sent to a worker middleware
*
* @export
* @interface WorkerAction
* @extends {Action}
*/
export interface WorkerAction extends Action {
/**
* The worker identifier, if you're using the single worker middleware,
* you'll just need to set this to any truthy value.
*/
workerId:string;
/**
* Set this to true to terminate the worker with the provided ID.
* If no such worker is found, the action is ignored.
*/
terminate?:boolean;
}
/**
* A worker registry interface.
* The interface represents a contract for a registry to manage multiple workers.
* @see {MultiWorkerMiddleware}
*/
export interface WorkerRegistry{
/**
* Returns a worker with a given key (identifier)
* @param {string} key the worker key
* @returns {Worker} the requested worker
* @throws {Error} if no worker with the provided key exists.
*/
get(key:string):Worker;
/**
* Registers a worker with a key and a file.
* @param {string} key the key to reference the worker by.
* @param {string|Worker} fileOrWorker either a file name to use with the worker
* or a worker object.
* @returns {WorkerRegistry} the current instance of the worker registry.
*/
register(key:string,fileOrWorker:string|Worker):WorkerRegistry;
/**
* Terminates a worker with a given key
* @param {string} key the worker key.
* @returns {WorkerRegistry} the current instance of the worker registry.
*/
terminate(key:string):WorkerRegistry;
}
/**
* Creates a single worker middleware.
* @param {Worker} worker the worker to use.
* @param {IStore} store the store instance.
* @returns {IMiddleware}
*/
export function WorkerMiddleware(worker:Worker,store:IStore):IMiddleware{
worker.addEventListener('open',function(e){
let action = {
type:'WorkerOpen',
data:1
};
store.dispatch(action);
});
worker.addEventListener('message',function(e){
let action = e.data;
store.dispatch(action);
});
worker.addEventListener('error',function(e){
let action = {
type:'WorkerError',
data:{
error:e.error,
message:e.message,
lineno:e.lineno,
colno:e.colno
}
};
store.dispatch(action);
});
return function(action:WorkerAction,store:IStore,next:IMiddlewareNext){
if (!action.workerId || !worker){
next(action);
}
((action.terminate && worker.terminate()) || worker.postMessage(action));
next();
};
}
/**
* Creates a multi-worker middleware.
*
* @param {WorkerRegistry} registry the worker registry to use.
* @param {IStore} store the state store instance
* @returns {Middleware} a multi-worker middleware
*/
export function MultiWorkerMiddleware(registry:WorkerRegistry,store: IStore):IMiddleware {
let workers:Dictionary<Worker> = {};
function addWorker(workerId:string){
let ww = workers[workerId] = registry.get(workerId);
ww.addEventListener('open',function(e){
let action = {
type:'WorkerOpen',
workerId,
data:{
workerId,
}
};
store.dispatch(action);
});
ww.addEventListener('message',function(e){
let action = e.data;
action.workerId = workerId;
store.dispatch(action);
});
ww.addEventListener('error',function(e){
let action = {
type:'WorkerError',
workerId,
data:{
workerId,
error:e.error,
message:e.message,
lineno:e.lineno,
colno:e.colno
}
};
store.dispatch(action);
});
return ww;
}
return function(action: WorkerAction, store: IStore, next:IMiddlewareNext) {
const workerId = action.workerId;
if (!workerId) {
next(action);
}
let ww = workers[workerId];
if (!ww && !action.terminate){
ww = addWorker(workerId);
}
if (action.terminate && ww){
ww.terminate();
delete ww[workerId];
next();
return;
}
ww.postMessage(action);
next();
}
}
/**
* Creates a basic implementation of a worker registry.
* @returns {WorkerRegistry}
*/
export function createWorkerRegistry():WorkerRegistry{
let o = null;
let dict:Dictionary<string> = {};
let workers:Dictionary<Worker> = {};
function register(key:string,file:string|Worker){
if (typeof file === "string"){
dict[key] = file;
}else if (typeof file === "object"){
workers[key] = file;
}
return o;
}
function get(key:string){
if (!workers[key]){
if (!dict[key]){
throw new Error(`No worker with key: ${key}`);
}
workers[key] = new Worker(dict[key]);
}
return workers[key];
}
function terminate(key:string){
let w = workers[key];
w && w.terminate();
return o;
}
o = {
register,
get,
terminate
};
return o;
}