@rosepetal/node-red-contrib-async-function 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/LICENSE +13 -0
- package/README.md +213 -0
- package/assets/example.png +0 -0
- package/nodes/async-function.html +600 -0
- package/nodes/async-function.js +351 -0
- package/nodes/lib/message-serializer.js +407 -0
- package/nodes/lib/module-installer.js +105 -0
- package/nodes/lib/shared-memory-manager.js +311 -0
- package/nodes/lib/timeout-manager.js +139 -0
- package/nodes/lib/worker-pool.js +533 -0
- package/nodes/lib/worker-script.js +192 -0
- package/package.json +41 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Copyright 2025 Rosepetal
|
|
2
|
+
|
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
you may not use this file except in compliance with the License.
|
|
5
|
+
You may obtain a copy of the License at
|
|
6
|
+
|
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
|
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
See the License for the specific language governing permissions and
|
|
13
|
+
limitations under the License.
|
package/README.md
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# node-red-contrib-async-function
|
|
2
|
+
|
|
3
|
+
Run heavy computations in Node-RED without slowing down your flows. This node works like the function node you already know, but keeps things responsive when the work gets heavy.
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
## What You Get
|
|
8
|
+
|
|
9
|
+
- Write JavaScript code that feels familiar—same as the function node.
|
|
10
|
+
- Run CPU-intensive tasks without blocking other flows.
|
|
11
|
+
- See real-time stats showing active workers and queue depth.
|
|
12
|
+
- Configure worker pools to match your workload.
|
|
13
|
+
- Handle bursts of messages smoothly with automatic queuing.
|
|
14
|
+
- Add external npm modules with auto-installation support.
|
|
15
|
+
|
|
16
|
+
## Before You Start
|
|
17
|
+
|
|
18
|
+
- Node.js 18 or newer (worker threads need it).
|
|
19
|
+
- Node-RED 2.0 or newer.
|
|
20
|
+
|
|
21
|
+
## How It Works
|
|
22
|
+
|
|
23
|
+
Drop an **async function** node into your flow. Write your code just like you would in a regular function node. The difference? Your code runs in a separate worker thread, so heavy operations won't freeze Node-RED.
|
|
24
|
+
|
|
25
|
+
## When to Use This
|
|
26
|
+
|
|
27
|
+
**Great For:**
|
|
28
|
+
- Calculating prime numbers, running crypto operations, or processing large datasets.
|
|
29
|
+
- Tasks that take more than 10 milliseconds to finish.
|
|
30
|
+
- Keeping your dashboard and other flows responsive during heavy work.
|
|
31
|
+
|
|
32
|
+
**Skip It For:**
|
|
33
|
+
- Simple math or quick transformations (the regular function node is faster).
|
|
34
|
+
- When you need `context`, `flow`, or `global` storage (coming in v2.0).
|
|
35
|
+
|
|
36
|
+
## Node Options
|
|
37
|
+
|
|
38
|
+
### Code & Behavior
|
|
39
|
+
- **Name** – Optional label for your canvas.
|
|
40
|
+
- **Function** – Your JavaScript code. Works with `async/await`, `return`, and `require()`.
|
|
41
|
+
- **Outputs** – How many output wires (0-10). Return an array for multiple outputs.
|
|
42
|
+
- **Timeout** – Maximum seconds to wait before killing the worker. Default: 30 seconds.
|
|
43
|
+
|
|
44
|
+
### Worker Pool
|
|
45
|
+
- **Workers** – Fixed number of worker threads (1-16). Each node maintains exactly this many workers. Default: 3.
|
|
46
|
+
- **Queue Size** – Messages to queue when all workers are occupied. Default: 100.
|
|
47
|
+
|
|
48
|
+
### Modules
|
|
49
|
+
Add external npm modules that will be available in your code. Similar to the standard Node-RED function node's module feature.
|
|
50
|
+
|
|
51
|
+
- **Module** – The npm package name (e.g., `lodash`, `moment`, `@scope/package`).
|
|
52
|
+
- **Import as** – Variable name to access the module in your code.
|
|
53
|
+
|
|
54
|
+
Modules are auto-installed to `~/.node-red` on first deploy if not already available. Use them directly in your code without `require()`:
|
|
55
|
+
|
|
56
|
+
```javascript
|
|
57
|
+
// With modules: lodash → _, moment → moment
|
|
58
|
+
const doubled = _.map(msg.payload, x => x * 2);
|
|
59
|
+
msg.timestamp = moment().format('YYYY-MM-DD');
|
|
60
|
+
return msg;
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Buffer Handling
|
|
64
|
+
- **Buffers** – Any `Buffer` in `msg` is transferred through shared memory (`/dev/shm` on Linux, otherwise `os.tmpdir()`), with base64 fallback if needed.
|
|
65
|
+
|
|
66
|
+
## Typical Flow
|
|
67
|
+
|
|
68
|
+
1. Add an **async function** node to your workspace.
|
|
69
|
+
2. Connect an Inject node (input) and a Debug node (output).
|
|
70
|
+
3. Write a simple script:
|
|
71
|
+
```javascript
|
|
72
|
+
msg.payload = msg.payload * 2;
|
|
73
|
+
return msg;
|
|
74
|
+
```
|
|
75
|
+
4. Deploy and trigger. Watch the status update in real time.
|
|
76
|
+
|
|
77
|
+
## What You Can Use in Your Code
|
|
78
|
+
|
|
79
|
+
**Available:**
|
|
80
|
+
- `msg` – The message object (must be serializable)
|
|
81
|
+
- `return` – Return a single message or array of messages
|
|
82
|
+
- `async/await` – For asynchronous operations
|
|
83
|
+
- `require()` – Load Node.js built-in or installed modules
|
|
84
|
+
- Configured modules – Available directly as variables (no require needed)
|
|
85
|
+
- `console` – Logging functions
|
|
86
|
+
- `setTimeout`, `setInterval` – Timers
|
|
87
|
+
|
|
88
|
+
**Not Available (Yet):**
|
|
89
|
+
- `context`, `flow`, `global` – Coming in v2.0
|
|
90
|
+
- `node` – Node instance methods
|
|
91
|
+
- Non-serializable objects (functions, symbols, etc.)
|
|
92
|
+
|
|
93
|
+
## Code Examples
|
|
94
|
+
|
|
95
|
+
### Simple Transformation
|
|
96
|
+
```javascript
|
|
97
|
+
msg.payload = msg.payload * 2;
|
|
98
|
+
return msg;
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Using External Modules
|
|
102
|
+
|
|
103
|
+
**Option 1: Configure in Setup tab (recommended)**
|
|
104
|
+
|
|
105
|
+
Add the module in the Modules section of the Setup tab, then use it directly:
|
|
106
|
+
```javascript
|
|
107
|
+
// Module configured: lodash → _
|
|
108
|
+
msg.payload = _.sortBy(msg.payload, 'name');
|
|
109
|
+
return msg;
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**Option 2: Traditional require()**
|
|
113
|
+
```javascript
|
|
114
|
+
const crypto = require('crypto');
|
|
115
|
+
|
|
116
|
+
msg.hash = crypto.createHash('sha256')
|
|
117
|
+
.update(msg.payload)
|
|
118
|
+
.digest('hex');
|
|
119
|
+
|
|
120
|
+
return msg;
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### CPU-Intensive Task (Won't Block!)
|
|
124
|
+
```javascript
|
|
125
|
+
function isPrime(n) {
|
|
126
|
+
if (n <= 1) return false;
|
|
127
|
+
for (let i = 2; i * i <= n; i++) {
|
|
128
|
+
if (n % i === 0) return false;
|
|
129
|
+
}
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const limit = msg.payload;
|
|
134
|
+
const primes = [];
|
|
135
|
+
|
|
136
|
+
for (let i = 2; i <= limit; i++) {
|
|
137
|
+
if (isPrime(i)) {
|
|
138
|
+
primes.push(i);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
msg.payload = primes;
|
|
143
|
+
return msg;
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Multiple Outputs
|
|
147
|
+
```javascript
|
|
148
|
+
if (msg.payload > 100) {
|
|
149
|
+
return [msg, null]; // Send to first output
|
|
150
|
+
} else {
|
|
151
|
+
return [null, msg]; // Send to second output
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Status Display
|
|
156
|
+
|
|
157
|
+
The node shows you what's happening in real time:
|
|
158
|
+
|
|
159
|
+
- **Active: 2/4** – 2 workers processing out of 4 total
|
|
160
|
+
- **Queue: 5** – 5 messages waiting
|
|
161
|
+
- **Green dot** – Normal operation
|
|
162
|
+
- **Yellow dot** – Queue filling up (>50 messages)
|
|
163
|
+
- **Red dot** – Queue almost full (>90%) or error
|
|
164
|
+
- **Ring** – All workers busy with a backlog
|
|
165
|
+
|
|
166
|
+
## Performance Notes
|
|
167
|
+
|
|
168
|
+
- Worker threads add about 5-10ms overhead per message.
|
|
169
|
+
- Best for operations taking more than 10ms to run.
|
|
170
|
+
- Each node maintains a fixed pool of workers—no startup delay or dynamic scaling overhead.
|
|
171
|
+
- Workers are dedicated per-node, ensuring predictable performance.
|
|
172
|
+
- **Binary Fast Path**: Buffers use shared memory transfer (base64 fallback), keeping messages responsive even with large payloads.
|
|
173
|
+
- Event loop never blocks, even when processing multi-MB binary data (images, files, etc.).
|
|
174
|
+
|
|
175
|
+
## Error Handling
|
|
176
|
+
|
|
177
|
+
Errors in your code get caught and sent to a Catch node:
|
|
178
|
+
|
|
179
|
+
```javascript
|
|
180
|
+
if (!msg.payload) {
|
|
181
|
+
throw new Error('Payload is required');
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Installation
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
cd ~/.node-red
|
|
189
|
+
npm install @rosepetal/node-red-contrib-async-function
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Restart Node-RED and find the node in the **function** category.
|
|
193
|
+
|
|
194
|
+
## Migration from Earlier Versions
|
|
195
|
+
|
|
196
|
+
If you're upgrading from a version that used `minWorkers` and `maxWorkers`:
|
|
197
|
+
- Your existing flows will automatically migrate to use the new `numWorkers` parameter
|
|
198
|
+
- The migration uses your previous `maxWorkers` value as the fixed worker count
|
|
199
|
+
- Check the Node-RED log for migration messages
|
|
200
|
+
- Edit your nodes to see the new simplified "Workers" configuration field
|
|
201
|
+
- **Note**: The new version uses a fixed worker pool instead of dynamic scaling for more predictable performance
|
|
202
|
+
|
|
203
|
+
## Contributing
|
|
204
|
+
|
|
205
|
+
Found a bug or have an idea? Open an issue or pull request on [GitHub](https://github.com/rosepetal-ai/node-red-contrib-async-function).
|
|
206
|
+
|
|
207
|
+
## License
|
|
208
|
+
|
|
209
|
+
Apache-2.0 © 2025 Rosepetal
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
**Built by [Rosepetal](https://www.rosepetal.ai)** – Making Node-RED flows faster and friendlier.
|
|
Binary file
|