Skip to content

Commit

Permalink
Add support of thread's error propagation
Browse files Browse the repository at this point in the history
  • Loading branch information
j-devel committed Mar 3, 2022
1 parent 478739e commit a54f327
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 32 deletions.
6 changes: 6 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,10 @@ class Thread {

if (this._isNode) {
_worker.on('message', e => this._onMessage(e));
_worker.on('error', e => this._onError(e));
} else {
_worker.onmessage = e => this._onMessage(e.data);
_worker.onerror = e => this._onError(e.data);
}
}
_onMessage(e) {
Expand All @@ -92,6 +94,10 @@ class Thread {
console.log('nop; invalid request id:', id);
}
}
_onError(e) {
console.log('_onError(): e:', e);
this._cancelPendingRequests();
}
_sendRequest(data, opts={}) {
const defaults = {
transferables: [],
Expand Down
79 changes: 60 additions & 19 deletions tests/browser/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<title>tests</title>
</head>
<body>
<script id="worker" type="javascript/worker">
<script id="simple" type="javascript/worker">
importScripts(`${baseUrl}/__atw.min.js`);
class MyThreadWorker extends AsyncThreadWorker.ThreadWorker {
onRequest(id, payload) { // impl
Expand All @@ -16,40 +16,81 @@
}
const _thw = new MyThreadWorker(self);
</script>
<script id="terminate" type="javascript/worker">
importScripts(`${baseUrl}/__atw.min.js`);
class MyThreadWorker extends AsyncThreadWorker.ThreadWorker {
onRequest(id, payload) { // impl
// ignore the request
}
}
const _thw = new MyThreadWorker(self);
</script>
<script id="onerror" type="javascript/worker">
importScripts(`${baseUrl}/__atw.min.js`);
class MyThreadWorker extends AsyncThreadWorker.ThreadWorker {
onRequest(id, payload) { // impl
if (payload === 2) throw 42;

this.sendResponse(id, payload + 100);
}
}
const _thw = new MyThreadWorker(self);
</script>

<script src="__atw.min.js"></script>
<script type="module">
const baseUrl = window.location.href.replace('/index.html', '');
const content = `const baseUrl = "${baseUrl}";\n`
+ document.querySelector('#worker').textContent;
const createThread = (baseUrl, idScript) => {
const content = `const baseUrl = "${baseUrl}";\n`
+ document.querySelector(idScript).textContent;

const objectUrl = window.URL.createObjectURL(
new Blob([content], {type: 'text/javascript'}));
const th = new AsyncThreadWorker.Thread(objectUrl);
window.URL.revokeObjectURL(objectUrl);
const objectUrl = window.URL.createObjectURL(
new Blob([content], {type: 'text/javascript'}));
const th = new AsyncThreadWorker.Thread(objectUrl);
window.URL.revokeObjectURL(objectUrl);

return th;
};

const baseUrl = window.location.href.replace('/index.html', '');
const thSimple = createThread(baseUrl, '#simple');
const thTerminate = createThread(baseUrl, '#terminate');
const thOnerror = createThread(baseUrl, '#onerror');

(async () => {
// test for 'simple'
const _result = [];
// test 'simple'
const resSimple = [];
for (let payload of ['a', 'b', 'c', 'd']) {
_result.push(await th.sendRequest(payload));
resSimple.push(await thSimple.sendRequest(payload));
}
thSimple.terminate();

// test for 'terminate'
setTimeout(() => th.terminate(), 50);
let ret;
// test 'terminate'
let resTerminate;
setTimeout(() => thTerminate.terminate(), 50);
try {
ret = await th.sendRequest(42); // will hang
resTerminate = await thTerminate.sendRequest(42); // will hang
} catch (err) {
ret = err;
resTerminate = err;
}

// test 'onerror'
const resOnerror = [];
for (let i = 0; i < 4; i++) {
try {
resOnerror.push(await thOnerror.sendRequest(i));
} catch (err) { // `_cancelPendingRequests()` kicks in
resOnerror.push(err);
break;
}
}

// store results
window['output'] = {
baseUrl,
result: {
simple: _result.join(''),
terminate: ret,
results: {
simple: resSimple.join(''), // 'ABCD'
terminate: resTerminate, // 'canceled:...'
onerror: resOnerror.length, // 3
},
};
})();
Expand Down
10 changes: 5 additions & 5 deletions tests/browser/main.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const { Server } = require('es-pack-js');
const libName = 'async-thread-worker';
const outDir = path.join(__dirname, '../../target');
const modPath = `${outDir}/${libName}.min.js`;
// const modPath = `${outDir}/${libName}.js`; // dev !!!!
//const modPath = `${outDir}/${libName}.js`; // dev

const tmpModPath = `${__dirname}/__atw.min.js`;

Expand Down Expand Up @@ -36,8 +36,8 @@ afterAll(async () => {

test('output', () => {
expect(typeof output).toBe('object');
expect(output.baseUrl.startsWith('http://')).toBe(true);
expect(output.baseUrl.startsWith('http://')).toBeTruthy();
});
test('simple', () => expect(output.result.simple).toBe('ABCD'));
test('api terminate() canceled', () =>
expect(output.result.terminate.startsWith('canceled:')).toBe(true));
test('simple', () => expect(output.results.simple).toBe('ABCD'));
test('terminate', () => expect(output.results.terminate.startsWith('canceled:')).toBeTruthy());
test('onerror', () => expect(output.results.onerror).toBe(3));
46 changes: 38 additions & 8 deletions tests/node/main.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,16 @@ const path = require('path');
const libName = 'async-thread-worker';
const outDir = path.join(__dirname, '../../target');
const __modPath = `${outDir}/${libName}.min.js`;
// const __modPath = `${outDir}/${libName}.js`; // dev !!!!
//const __modPath = `${outDir}/${libName}.js`; // dev
const Mod = require(__modPath);

test('load', () => {
expect(Mod.hasOwnProperty('Thread')).toBe(true);
expect(Mod.hasOwnProperty('ThreadWorker')).toBe(true);
expect(Mod.hasOwnProperty('Thread')).toBeTruthy();
expect(Mod.hasOwnProperty('ThreadWorker')).toBeTruthy();
});

// Skip if v10.x which requires `--experimental-worker` for 'worker_threads'
if (process.version > 'v12.') {
const test00 = () => {}; // just for easy toggling on/off tests

// kludge: make sure `global.require` inside `Mod` is available
global.require = require;
Expand Down Expand Up @@ -104,13 +103,44 @@ const _thw = new Mod.ThreadWorker(this, { isNode: true });
} catch (err) {
result = err;
}
expect(result.startsWith('canceled:')).toBe(true);
expect(result.startsWith('canceled:')).toBeTruthy();
});

//

test00('-', async () => {
});
test00('-', async () => {
test('_onError()', async () => {
const content = `
const Mod = require('${__modPath}');
class MyThreadWorker extends Mod.ThreadWorker {
onRequest(id, payload) { // impl
if (payload === 2) throw 42;
this.sendResponse(id, payload + 100);
}
}
const _thw = new MyThreadWorker(this, { isNode: true });
`;

const th = new Mod.Thread(content, {
isNode: true,
optsNode: { eval: true },
});

const results = [];
for (let i = 0; i < 4; i++) {
try {
results.push(await th.sendRequest(i));
} catch (err) { // `_cancelPendingRequests()` kicks in
results.push(err);
break;
}
}

expect(results.length).toBe(3);
expect(results[0]).toBe(100);
expect(results[1]).toBe(101);
expect(results[2].startsWith('canceled:')).toBeTruthy();
});
}

0 comments on commit a54f327

Please sign in to comment.