Welcome to ciysys blog

Running a list of function

Published on: 21th Sep 2024

Overview

In any system, a main process requires to run a series of sub-process to process the data. For example, in Node Express web framework, it allows the developer to have middleware to pre-process the request. The developer will have to decide whether it should continue with next process/step. This is useful and convenient to split a huge process into multiple smaller sub-processes.

Let's try to develop a class that keeps multiple sub-processes (i.e., function) for a process and run all functions against the data.

Sample class

The code will explain everything.

class funcList {
    _fn_exec_list = [];

    /**
    * Add function to tbe execution list.
    * @param {Function|Function[]} fn
    */
    add(fn) {
        if (fn) {
            if (typeof fn == 'function') {
                // if it is a Function type, append it to the _fn_list.
                this._fn_exec_list.push(fn);
            }
            else if (Array.isArray(fn)) {
                // if it is an Array, add each function to the _fn_list.
                fn.forEach(a2 => {
                    this._fn_exec_list.push(a2);
                });
            }
        }
    }

    /**
    * Run all functions.
    * @param {Object} [user_data] - the user data to be processed/referenced by each function.
    * @param {Function} [oncompletion] - the last function to be executed.
    */
    run(user_data, oncompletion) {
        // capture the number of functions to be executed.
        const mx = this._fn_exec_list.length;
        let idx = -1;

        // we need a context to keep the user's data
        const context = {
            step: idx,
            user_data: user_data,
        };

        if (mx > 0) {
            // run the functions one after another.
            const next_fn = () => {
                // move to next function in the array.
                idx++;

                // the step value will be maintained here and it
                // cannot be changed outside of this function.
                context.step = idx;

                let fn;
                if (idx < mx) {
                    fn = this._fn_exec_list[idx];
                }
                else {
                    // if there is no more function to be executed,
                    // run the oncompletion callback and exit.
                    if (oncompletion) {
                        oncompletion();
                    }
                    return null;
                }

                // execute the function (fn) and passes the 'next_fn' function as parameter.
                // As a result, the 'fn' will have to decide whether to continue next
                // step or stop the process.
                fn(context, next_fn);
            }

            // execute the first function
            next_fn();
        }
        else if (oncompletion) {
            oncompletion();
        }
    }
}

//------------------------------------------------------------------------------
// create a new instance.
const fn_list = new funcList();

// add 1 function to show the opening balance.
fn_list.add((context, next) => {
    console.log(`step:${context.step}, opening balance: ${context.user_data.balance}`);
    next();
})

// add 3 functions to process the balance by adding interest.
fn_list.add([
    (context, next) => {
        context.user_data.balance += 3;
        console.log(`step:${context.step}, balance: ${context.user_data.balance}`);
        next();
    },
    (context, next) => {
        context.user_data.balance += 5;
        console.log(`step:${context.step}, balance: ${context.user_data.balance}`);
        next();
    },
    (context, next) => {
        context.user_data.balance += 10;
        console.log(`step:${context.step}, balance: ${context.user_data.balance}`);
        next();
    },
]);

// here's an account to be processed by the above process.
const account = {
    account_number: '1234',
    balance: 1000,
};

fn_list.run(account,
    () => {
        console.log('final step');
    });

/*
//output:
step:0, opening balance: 1000
step:1, balance: 1003
step:2, balance: 1008
step:3, balance: 1018
final step
*/

Use case

Conclusion

This class provides a convenient way to swap the sub-process sequence, removal or adding of sub-process.

Related posts

Jump to #JAVASCRIPT blog

Jump to #NODEJS blog

Author

Lau Hon Wan, software developer.