node-thread-decorator 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -0
- package/package.json +19 -0
- package/src/thread.decorator.d.ts +12 -0
- package/src/thread.decorator.ts +67 -0
- package/tsconfig.json +29 -0
package/README.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Node Thread Decorator
|
|
2
|
+
|
|
3
|
+
[![node version][node-image]][node-url]
|
|
4
|
+
|
|
5
|
+
[node-image]: https://img.shields.io/badge/node.js-%3E=_18.0-green.svg?style=flat-square
|
|
6
|
+
[node-url]: http://nodejs.org/download/
|
|
7
|
+
|
|
8
|
+
### Install
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
$ npm i -S node-thread-decorator
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
### Usage
|
|
15
|
+
|
|
16
|
+
- Import
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { RunInNewThread } from "node-thread-decorator";
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { Controller, Get } from '@nestjs/common';
|
|
24
|
+
|
|
25
|
+
import { RunInNewThread } from 'node-thread-decorator';
|
|
26
|
+
import { IHealthAdapter } from './adapter';
|
|
27
|
+
|
|
28
|
+
@Controller()
|
|
29
|
+
export class HealthController {
|
|
30
|
+
/**
|
|
31
|
+
* Blocks the main thread for a specified duration in milliseconds.
|
|
32
|
+
* This function runs in a new process due to the decorator `@RunInNewProcess`.
|
|
33
|
+
*
|
|
34
|
+
* @param {number} milliseconds - The time to block the thread in milliseconds.
|
|
35
|
+
*/
|
|
36
|
+
@RunInNewThread()
|
|
37
|
+
blockMainThread(milliseconds: number) {
|
|
38
|
+
const start = Date.now();
|
|
39
|
+
while (Date.now() - start < milliseconds) {
|
|
40
|
+
// Looping until time has passed
|
|
41
|
+
}
|
|
42
|
+
console.log("Finished new process", process.pid);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Endpoint that returns health information of the service.
|
|
47
|
+
* It will block the main thread for 10 seconds to simulate a heavy operation.
|
|
48
|
+
*
|
|
49
|
+
* @returns {Promise<string>} - A promise that resolves when the health check is completed.
|
|
50
|
+
*/
|
|
51
|
+
@Get(['/health', '/'])
|
|
52
|
+
async getHealth(): Promise<string> {
|
|
53
|
+
console.log("Started process", process.pid);
|
|
54
|
+
this.blockMainThread(10000); // Simulating blocking the thread for 10 seconds
|
|
55
|
+
console.log("Health check completed");
|
|
56
|
+
return "APP UP!"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
The following is a list of all the people that have contributed Node Thread Decorator. Thanks for your contributions!
|
|
63
|
+
|
|
64
|
+
[<img alt="mikemajesty" src="https://avatars1.githubusercontent.com/u/11630212?s=460&v=4&s=117" width="117">](https://github.com/mikemajesty)
|
|
65
|
+
|
|
66
|
+
## License
|
|
67
|
+
|
|
68
|
+
It is available under the MIT license.
|
|
69
|
+
[License](https://opensource.org/licenses/mit-license.php)
|
package/package.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "node-thread-decorator",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "node thread decorator",
|
|
5
|
+
"main": "dist/thread.decorator.js",
|
|
6
|
+
"types": "dist/thread.decorator.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [],
|
|
11
|
+
"author": "",
|
|
12
|
+
"license": "ISC",
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"colorette": "^2.0.20"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@types/node": "^22.13.7"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module providing decorators for execution in separate threads.
|
|
3
|
+
*/
|
|
4
|
+
declare module 'node-thread' {
|
|
5
|
+
/**
|
|
6
|
+
* Executes the decorated method in a separate thread.
|
|
7
|
+
*
|
|
8
|
+
* @param timeout - Maximum time in milliseconds for the thread execution. If not provided, there is no timeout.
|
|
9
|
+
* @returns A method decorator that executes the method in a separate thread.
|
|
10
|
+
*/
|
|
11
|
+
export function RunInNewThread(timeout?: number): MethodDecorator;
|
|
12
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { red } from 'colorette';
|
|
2
|
+
import { Worker } from 'worker_threads';
|
|
3
|
+
|
|
4
|
+
export function RunInNewThread(timeout?: number) {
|
|
5
|
+
return function (target: object, key: string, descriptor: PropertyDescriptor): PropertyDescriptor {
|
|
6
|
+
const originalMethod = descriptor.value;
|
|
7
|
+
|
|
8
|
+
// Modifying the descriptor to replace the original method with one that runs in a worker thread
|
|
9
|
+
descriptor.value = function (...args: unknown[]) {
|
|
10
|
+
// Convert the method code to string to send to the worker
|
|
11
|
+
const fnCode = originalMethod.toString();
|
|
12
|
+
|
|
13
|
+
// Create a new Worker instance to run the method in a separate thread
|
|
14
|
+
const worker = new Worker(`${__dirname}/thread.js`);
|
|
15
|
+
|
|
16
|
+
let timeoutId: NodeJS.Timeout | null = null;
|
|
17
|
+
|
|
18
|
+
// Send the method code and arguments to the worker
|
|
19
|
+
worker.postMessage([fnCode, args]);
|
|
20
|
+
|
|
21
|
+
// Set a timeout if specified
|
|
22
|
+
if (timeout) {
|
|
23
|
+
timeoutId = setTimeout(() => {
|
|
24
|
+
const className = target.constructor.name;
|
|
25
|
+
const methodName = key;
|
|
26
|
+
const error = new Error('worker execution timed out.');
|
|
27
|
+
Object.assign(error, { context: `${className}/${methodName}` });
|
|
28
|
+
|
|
29
|
+
console.error(error);
|
|
30
|
+
worker.terminate(); // Terminate the worker if it times out
|
|
31
|
+
}, timeout);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Handle messages received from the worker (either success or error)
|
|
35
|
+
worker.on('message', (value: { error: Error; success: unknown }) => {
|
|
36
|
+
if (timeoutId) clearTimeout(timeoutId); // Clear the timeout if the worker finishes early
|
|
37
|
+
if (value.error) {
|
|
38
|
+
return Promise.reject(value.error); // Reject if there's an error
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (value.success) {
|
|
42
|
+
return Promise.resolve(value.success); // Resolve with the worker's result
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Handle errors emitted by the worker thread
|
|
47
|
+
worker.on('error', (err: Error) => {
|
|
48
|
+
if (timeoutId) clearTimeout(timeoutId); // Clear timeout on error
|
|
49
|
+
if (err.name === 'ReferenceError') {
|
|
50
|
+
console.error(red('worker error '), err);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
console.error(red('worker error '), err?.message ?? err);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Handle worker exit (whether successful or with an error code)
|
|
57
|
+
worker.on('exit', (code: number) => {
|
|
58
|
+
if (timeoutId) clearTimeout(timeoutId); // Clear timeout on worker exit
|
|
59
|
+
if (code !== 0) {
|
|
60
|
+
console.error(red(`worker stopped with exit code ${code}`)); // Log if the worker exits with a non-zero code
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
return descriptor;
|
|
66
|
+
};
|
|
67
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"module": "commonjs",
|
|
4
|
+
"declaration": true,
|
|
5
|
+
"removeComments": true,
|
|
6
|
+
"emitDecoratorMetadata": true,
|
|
7
|
+
"experimentalDecorators": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"allowSyntheticDefaultImports": true,
|
|
10
|
+
"target": "es2017",
|
|
11
|
+
"resolveJsonModule": true,
|
|
12
|
+
"sourceMap": true,
|
|
13
|
+
"outDir": "./dist",
|
|
14
|
+
"baseUrl": "./",
|
|
15
|
+
"incremental": true,
|
|
16
|
+
"strict": true,
|
|
17
|
+
"skipLibCheck": true,
|
|
18
|
+
"strictNullChecks": true,
|
|
19
|
+
"noImplicitAny": true,
|
|
20
|
+
"strictBindCallApply": false,
|
|
21
|
+
"forceConsistentCasingInFileNames": false,
|
|
22
|
+
"noFallthroughCasesInSwitch": false,
|
|
23
|
+
"paths": {
|
|
24
|
+
"@/*": [
|
|
25
|
+
"./src/*"
|
|
26
|
+
]
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|