-
Notifications
You must be signed in to change notification settings - Fork 259
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Question: why next node is triggered without previous node call output.sendDone
yet
#1047
Comments
Human: GPT4: Let's clarify:
Given the confusion, let's address the core issue: If
If none of these seem to be the cause, there might be something deeper going on in the way your graph is set up or how the components are being used. You may want to set up a minimal reproducible example to narrow down the issue. Human: GPT4: Here's a basic approach to IP forwarding: this.process((input, output) => {
// If we have data on 'in' port
if (input.hasData('in')) {
const data = input.getData('in');
// Forward the IP to the 'out' port
output.send({
out: data
});
// Signal that processing for this IP is done
output.done();
}
}); However, if a component does not call Regarding the Remember, NoFlo's packet management can involve more than just data. There are different types of IPs, including:
If you're seeing unexpected behavior, make sure to account for all these different IPs and how they might be influencing the flow in your graph. Human: The answer is in noflo/src/lib/Component.js , if Component's process method (saved in // ### Handling IP objects
//
// The component has received an Information Packet. Call the
// processing function so that firing pattern preconditions can
// be checked and component can do processing as needed.
/**
* @param {IP} ip
* @param {InPort} port
* @returns {void}
*/
handleIP(ip, port) {
if (!port.options.triggering) {
// If port is non-triggering, we can skip the process function call
return;
}
if ((ip.type === 'openBracket') && (this.autoOrdering === null) && !this.ordered) {
// Switch component to ordered mode when receiving a stream unless
// auto-ordering is disabled
debugComponent(`${this.nodeId} port '${port.name}' entered auto-ordering mode`);
this.autoOrdering = true;
}
// Initialize the result object for situations where output needs
// to be queued to be kept in order
/** @type {ProcessResult} */
let result = {};
if (this.isForwardingInport(port)) {
// For bracket-forwarding inports we need to initialize a bracket context
// so that brackets can be sent as part of the output, and closed after.
if (ip.type === 'openBracket') {
// For forwarding ports openBrackets don't fire
return;
}
if (ip.type === 'closeBracket') {
// For forwarding ports closeBrackets don't fire
// However, we need to handle several different scenarios:
// A. There are closeBrackets in queue before current packet
// B. There are closeBrackets in queue after current packet
// C. We've queued the results from all in-flight processes and
// new closeBracket arrives
const buf = port.getBuffer(ip.scope, ip.index);
const dataPackets = buf.filter((p) => p.type === 'data');
if ((this.outputQ.length >= this.load) && (dataPackets.length === 0)) {
if (buf[0] !== ip) { return; }
if (!port.name) { return; }
// Remove from buffer
port.get(ip.scope, ip.index);
const bracketCtx = this.getBracketContext('in', port.name, ip.scope, ip.index).pop();
bracketCtx.closeIp = ip;
debugBrackets(`${this.nodeId} closeBracket-C from '${bracketCtx.source}' to ${bracketCtx.ports}: '${ip.data}'`);
result = {
__resolved: true,
__bracketClosingAfter: [bracketCtx],
};
this.outputQ.push(result);
this.processOutputQueue();
}
// Check if buffer contains data IPs. If it does, we want to allow
// firing
if (!dataPackets.length) { return; }
}
}
// Prepare the input/output pair
const context = new ProcessContext(ip, this, port, result);
const input = new ProcessInput(this.inPorts, context);
const output = new ProcessOutput(this.outPorts, context);
try {
// Call the processing function
if (!this.handle) {
throw new Error('Processing function not defined');
}
const res = this.handle(input, output, context);
if (res && res.then) {
// Processing function returned a Promise
res.then(
(data) => output.sendDone(data),
(err) => output.done(err),
);
}
} catch (e) {
this.deactivate(context);
output.sendDone(e);
}
if (context.activated) { return; }
// If receiving an IP object didn't cause the component to
// activate, log that input conditions were not met
if (port.isAddressable()) {
debugComponent(`${this.nodeId} packet on '${port.name}[${ip.index}]' didn't match preconditions: ${ip.type}`);
return;
}
debugComponent(`${this.nodeId} packet on '${port.name}' didn't match preconditions: ${ip.type}`);
} GPT4: The part you highlighted is particularly interesting: const res = this.handle(input, output, context);
if (res && res.then) {
// Processing function returned a Promise
res.then(
(data) => output.sendDone(data),
(err) => output.done(err),
);
} From the code, it's clear that if your process function ( This behavior means that even if you don't explicitly send data, NoFlo will automatically send whatever your Promise resolves to (including The implication of this is significant: if you don't want to send an output immediately after processing your
The key takeaway is that when working with NoFlo, especially with |
This makes async process function and normal process function have different semantic about return value, maybe this should be changed in the core? |
A workaround on user-side is to use legacy |
I connect a TextField node to the ResultText node, and connect ResultText node to an Output node, and found even in TextField's process method I haven't call
output.sendDone
yet, ResultText's process method will keeps running when TextField receive IP (send byfbpGraph.addInitial
to that TextField node). Why will ResultText be triggered by IP send to TextField node? And I found ResultText node is also triggering Output node by sendingnull
to Output node's in port, why?GPT4:
Your described behavior and the code hint towards a few NoFlo nuances. Here's a breakdown:
How does NoFlo handle data flow?
The NoFlo runtime processes nodes in a data-driven manner. When you send an Information Packet (IP) to an input port of a component (e.g.,
TextField
), NoFlo tries to activate the component by calling itsprocess
method. This behavior ensures that data flows through the graph.Why is
ResultText
activated whenTextField
receives an IP?If you've connected
TextField
's output port toResultText
's input port, sending an IP toTextField
will activate it. WhenTextField
sends an output (even if it doesn't callsendDone
), that output activates the next node in line (ResultText
in this case).Why is
null
sent to the Output node?If
TextField
sends anull
(or any other value) toResultText
, andResultText
doesn't have the required data to process further (i.e., your checks likeif (!input.hasData('in')) return;
), it won't calloutput.sendDone(...)
. However, the IP might have already been forwarded toResultText
's output port. If you've connectedResultText
toOutput
, thenOutput
would receive thatnull
.Here are a few steps to help debug and possibly rectify the issue:
Log IPs: Start by logging every IP that
TextField
sends out. This way, you can ensure thatTextField
is only sending what you expect.Handle IPs Carefully: In your
ResultText
process function, you're usinginput.getData('in')
. This method not only fetches the IP but also removes it. If the conditions aren't right for processing that IP, it's essentially lost. Consider usinginput.get('in')
to peek at the IP without removing it.IP Forwarding: Ensure you're only sending the IPs you intend to send. NoFlo's packet forwarding can sometimes lead to unintended data being sent if you're not careful. Ensure that any IP you send out is intended and controlled.
Check Connections: Ensure your FBP graph connections are correctly made. An unintended connection might be causing the issues.
If you're still facing the issue after checking the above points, it might be helpful to look at the broader context, such as how your FBP graph is set up, how you're sending initial IPs, and if other components might be influencing this behavior.
The text was updated successfully, but these errors were encountered: