Extensions/AsyncOutput
ComfyUI Extension

AsyncOutput

Simple collect all batch output, then convert to a string list, like ECMAScript Promise.all

By teddy1565·Created 5 months ago·Updated 5 months ago· 0
teddy1565/ComfyUI-AsyncOutput
Nodes18
On cloudLocal install
CategoryAsyncOutput/Deprecated(ComfyUI NOT Turing complete)/String, AsyncOutput/Deprecated(ComfyUI NOT Turing complete)/Global
Stars0
Updated5 months ago

Nodes (18)

Output

The 'finally' clause of your ComfyUI batch loop

AsyncOutput/Deprecated(ComfyUI NOT Turing complete)/String
Collection

Park every string a batch run produces, then do something with all of them later

AsyncOutput/Deprecated(ComfyUI NOT Turing complete)/String
String List To String

Join a list of strings

AsyncOutput/Deprecated(ComfyUI NOT Turing complete)/String
Conditions

A 'do it once after N runs' gate for your ComfyUI graph

AsyncOutput/Deprecated(ComfyUI NOT Turing complete)/String
AsyncOutput[module_config] Global Auto Reset

The switch that decides whether AsyncOutput forgets everything between runs

AsyncOutput/Deprecated(ComfyUI NOT Turing complete)/Global
AsyncOutput[module_config] Global Memory Reset

The 'clear the AsyncOutput brain' button for when your loop goes weird

AsyncOutput/Deprecated(ComfyUI NOT Turing complete)/Global/ManualOperation
Init Singal Output

A one-shot 'workflow just started' pulse, spelled Singal

AsyncOutput/Deprecated(ComfyUI NOT Turing complete)/WorkFlowTool
Batch Text Yield With Batch

Feed one prompt per generation, paced by a loop, without a scheduler node

AsyncOutput/Deprecated(ComfyUI NOT Turing complete)/WorkFlowTool
Remote Collect

A counter your other workflow can feed, so a loop knows how far along it is

AsyncOutput/Deprecated(ComfyUI NOT Turing complete)/RemoteControl
Remote Trigger

Read the loop's odometer and decide whether to keep going

AsyncOutput/Deprecated(ComfyUI NOT Turing complete)/RemoteControl
Active error triggering

Raise an error on purpose when a signal fires

AsyncOutput/BatchIterator/SystemTools
BatchIterator GlobalCache Clear

The BatchIterator 'forget everything' node — and yes, it nags you on purpose

AsyncOutput/BatchIterator/SystemTools
Multiline Text Iterator

Queue a list of prompts and let each run grab the next one, automatically

AsyncOutput/BatchIterator/String
BatchIterator Store Probe

Peek inside the BatchIterator's hidden memory while your loop runs

AsyncOutput/BatchIterator/SystemTools
BatchIterator String Callback

Read the gathered strings when the emitter says it's time

AsyncOutput/BatchIterator/String
BatchIterator String Collection

The BatchIterator family's 'stash this string' node

AsyncOutput/BatchIterator/String
BatchIterator String Emitter

Count ticks, fire once when you hit the target, and (bonus) abort on -999

AsyncOutput/BatchIterator/String
StringList To String

The clean copy of the list-to-string join utility — use this one, not the deprecated twin

AsyncOutput/Utils/String
Readme

��# ComfyUI-AsyncOutput

Simple Source Code

class AsyncOutputCollectionNode:
    def __init__(self):
        pass

    @classmethod
    def INPUT_TYPES(s):
        return {
            "required": {
                "incoming_input": ("STRING", { "forceInput": True }),
                "key_id": ("STRING", {})
            },
            "hidden": {
                "unique_id": "UNIQUE_ID"
            }
        }
    
    RETURN_TYPES = ("STRING",)
    RETURN_NAMES = ("passthough_output",)
    CATEGORY = f'{MAIN_CATEGORY}/String'
    FUNCTION = "collect"
    
    def collect(self, incoming_input, key_id, unique_id):
        global ASYNC_OUTPUT_STORAGE_DATA
        
        if key_id == "":
            raise Exception("ERROR: key_id property not set.")
        
        if key_id not in ASYNC_OUTPUT_STORAGE_DATA:
            ASYNC_OUTPUT_STORAGE_DATA[key_id] = []
        
        ASYNC_OUTPUT_STORAGE_DATA[key_id].append(incoming_input)
        
        return (incoming_input,)
    
    @classmethod
    def IS_CHANGED(s, incoming_input, key_id, unique_id):
        return float('nan')

Intro

Simple collect all batch output, then convert to a string list

Like ECMAScript Promise.all

const tasks_queue: Array<Promise<string>> = [];
for (let i = 0; i < n; i++) {
    const task = new Promise<string>((resolve, reject) => {
        /* Your jobs, just a mock, */
    });
    tasks_queue.push(task);
}

/*
results will be an Array<string>
*/
Promise.all(tasks_queue).then((results) => {
    const ffmpeg_concat_input_str = results.join("\n");
    // others
})

Parmeters

  • key_id: persistence memory dict key

Node

  • String
    • AsyncOutputCollect(Collection)
      • Receive other node 'string' output, persistence memory
      • Output is 'string', passthrough your input
    • AsyncOutputEmitter(Conditions)
      • If conditions is true, Output Boolean, else always false.
    • AsyncOutputCallback(Output)
      • Receive Emitter Output, If true, will output dict[key_id], then delete dict[key_id]
      • If the received value is false, then the subsequent downstream process will be blocked directly.
      • (comfy_execution.graph.ExecutionBlocker(None), )
  • Utils
    • String List To String
      • Convert String List to String

Others (use JS illustrate)

String List to String

const string_list = ["abc", "edf", "jqk"];
const str = string_list.join("<your_input>");

AsyncOutputCollect(Collection)

const collect_list = [];

for (let i = 0; i < n; i++) {
    const task = new Promise();
    collect_list.push(task);
}

AsyncOutputEmitter(Conditions)

const collect_list = [];
const spec_task_n = 10;
const mode = "exact";
const e = new EventEmitter();

function* collect_task(collect_list) {
    for (let i = 0; i < n; i++) {
        const task = new Promise();
        collect_list.push(task);
        yield i;
    }
}

current_exec = collect_task(collect_list);

let it = current_exec.next();


if (mode === "exact") { // exact mode
    while (it.done == false && it.value != spec_task_n) {
        it = current_exec.next();
        e.emit("trigger_result", false);
    }
} else if (mode === "greater_than_or_equal") {  // greater_than_or_equal mode
    while (it.done == false && it.value <= spec_task_n) {
        it = current_exec.next();
        e.emit("trigger_result", false);
    }
}

if (it.value == spec_tack_n) {
    e.emit("trigger_result", true);
}


AsyncOutputCallback(Output)


const comfy_eventloop_emitter = new EventEmitter();

e.on("trigger_result", (trigger_signal) => {
    if (trigger_signal === true) {
        const str_list = callback();
        comfy_eventloop_emitter.emit("next", str_list);
    }
});