OBS: This is not talking about the flow/wiring order resolution, but the static references resolutions (nodes used by other nodes in their config. E.g. node A is set with node B using a typed input)
Nodes that include static references(id) to other nodes in their configuration must access the referenced instance during initialization. If the referenced node hasn’t been initialized yet, calling RED.nodes.get(config.referenceId) will be null/undefined
When the reference is a config node I dont have problems because config nodes are initialized before. However, if a non config node references another non config node, because the node instances were not sorted and instantiated based on their static dependencies, the initialization of the referenced instance can happen after the node that references it.
I have a node that reference different non config nodes. If the node I used as reference was not instantiated while my node instance is being created, then my node won't be initialized correctly.
Examples: 1 - All the following nodes were referenced as a "config" of another node. It doesn't matter if they are config nodes or not.
A depends on B
B depends on C
C depends on D
C depends on E
There are no cycles. Therefore, the order of instantiation/function call is: E -> D -> C -> B -> A
2 - When cycles are found, a runtime and editor error are thrown with the following message: Remove the following cycles and try again: [A_id, B_id, C_id]
A depends on B
B depends on C
C depends on A
The main benefit would be early validation + deterministic initialization. Without it, developers are currently using the following workarounds:
Delay setup until the first message (hacky, error-prone)
Require ugly retry logic (checking RED.nodes.get() until the reference appears)
Implementing what should be a core pre-setup functionality by themselves
Note that the editor sorts nodes during a deployment to first instantiate all config nodes and then regular nodes. If your problem is related to a regular node (the referenced one), we can still add a filter to solve it.
As above, Config Nodes are "Started" first - that's by design.
But if your config Nodes do async work that is not yet completed, before it has had a chance to service the subscriber Nodes (lets say the config Nodes are attempting to connect to a 3rd party network service), then your subscribers may fail - as the config Node is not yet ready to provide service (i.e its doing something async, before its truly ready)
I tend to employ the register, de-register approach for this.
/* Config Node */
module.exports = function (RED) {
const init = function (config) {
const self = this;
self.subscribers = {};
self.registerSubscriber = (NodeID, Callback) => {
self.subscribers[NodeID] = { Callback };
if (self.ready) {
Callback({
some_state_object
});
}
};
self.deRegisterSubscriber = (NodeID) => {
delete self.subscribers[NodeID];
};
/* Or some event in your logic */
self.onReady(() => {
Object.values(self.subscribers).forEach((S) => {
S.Callback({
some_state_object
});
});
});
};
};
How do you do when a non config node reference another non config node? With typed input fields we can select non config nodes when opening a node's form
But in the HTML, you can check if the Node type exists in the flow perhaps?
and warn the user if not found - prompting them to first instantiate the dependancy Node?
Im not sure how you are thinking about using this filter, but I don't think it would work when a referenced node can have another reference. In this scenario which node is instantiated first (A, B or C)? This type of problem is solved using topological sorting
I know topological sorting can't be used in the flow because there can exist cycles (it is not a DAG). But there can exist a smaller graph for static references (ids), that would be used to compute the topological order of non config nodes used in the configuration of other non config nodes. Couldn't it?
You mean for the runtime. Ok it's very simple: no way.
The only topology there is, is to start the global flow first (which contains global config nodes). From memory it is designated this way because the nodes do not have their own initialization. I mean that the runtime does not know if a node is fully initialized or not. Moreover, this would complicate and slow the startup of flows.
This isn't an answer. I would like to know a "why".
Is it technically impossible? If yes, why? Or is it because we don't know or don't want to do it?
obs: just to be assertive. Im not trying to frame or expose anybody. Don't get me wrong or feel threatened, please. I really just want to see if I can help to improve node-red.
I believe this would be possible if instantiation and wiring were handled in separate phases. The idea is to build a small DAG to determine the correct instantiation order for nodes that are referenced in other nodes’ configurations. These references could point to either config nodes or non-config nodes. With this approach, when my node is constructed, its dependency instances would already be available in the constructor. This would make it possible to properly compose nodes.
And again, I'm not talking about changing the flow/wiring order