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
- Processing HTTP request - checking the request, adding new header, etc.
- Data validation process - adding new rule and removing old rule with ease.
- Data processing - process the data based on different runtime value and configuration.
- Upon initializing data entry form - loading the drop down list item and setup the form.
- Upon cleaning up a data entry form before closing - removing the data cache in the localStorage, notifying the server that the form is closing or calling any cleanup function that was added during the user interaction.
- Run data exchange one after another - for example, you want to sends the new changes to another server one after another to avoid flooding the receiving server. This is similar like running a single threaded queue.
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.