/
index.js
66 lines (63 loc) · 1.98 KB
/
index.js
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
import {
useEffect,
useMemo,
useRef,
useReducer,
} from 'react';
const initialState = { result: null, error: null };
const reducer = (state, action) => {
switch (action.type) {
case 'init':
return initialState;
case 'result':
return { result: action.result, error: null };
case 'error':
return { result: null, error: 'error' };
case 'messageerror':
return { result: null, error: 'messageerror' };
default:
throw new Error('no such action type');
}
};
const createWorker = (func) => {
if (func instanceof Worker) return func;
if (typeof func === 'string' && func.endsWith('.js')) return new Worker(func);
const code = [
`self.func = ${func.toString()};`,
'self.onmessage = async (e) => {',
' const r = self.func(e.data);',
' if (r[Symbol.asyncIterator]) {',
' for await (const i of r) self.postMessage(i)',
' } else if (r[Symbol.iterator]){',
' for (const i of r) self.postMessage(i)',
' } else {',
' self.postMessage(await r)',
' }',
'};',
];
const blob = new Blob(code, { type: 'text/javascript' });
const url = URL.createObjectURL(blob);
return new Worker(url);
};
export const useWorker = (func, input) => {
const [state, dispatch] = useReducer(reducer, initialState);
const worker = useMemo(() => createWorker(func), [func]);
const lastWorker = useRef(null);
useEffect(() => {
lastWorker.current = worker;
let dispatchSafe = action => dispatch(action);
worker.onmessage = e => dispatchSafe({ type: 'result', result: e.data });
worker.onerror = () => dispatchSafe({ type: 'error' });
worker.onmessageerror = () => dispatchSafe({ type: 'messageerror' });
const cleanup = () => {
dispatchSafe = () => null; // we should not dispatch after cleanup.
worker.terminate();
dispatch({ type: 'init' });
};
return cleanup;
}, [worker]);
useEffect(() => {
lastWorker.current.postMessage(input);
}, [input]);
return state;
};