'In JavaScript, how to execute next function from an array of functions

I have an array of functions, as in:

funcArray = [func1, func2, func3];

When in a given function, I want to execute the next function in the array. How do I do this? Here is my basic skeleton:

function func1() {
  // I get current function caller
  var currentFunc = func1.caller;

  // I want to execute the next function. Happens to be func2 in the example.

}

I cannot use indexOf function, as one would for an array of strings or numbers. NOTE: This question appears to be similar to this and the one it refers to. However, it is a different question.

I want to alter the sequence of processing by merely modifying the array. That's the goal. A possibly more efficient approach would be appreciated.

Clarification: Based upon some of the comments: funcArray is global.

The goal is to implement middleware for a Node.js HTTP module in as simple and efficient a manner as possible without using any third-party modules.



Solution 1:[1]

Unless func1 closes over funcArray, you cannot have it reach out and find func2 and execute it, nor should you. Even if func1 does close over funcArray, it would be poor separation of concerns for func1 to reach out and find itself in funcArray and then execute func2.

Instead, have other code that's in charge of running the functions.

If they're synchronous

If the functions complete their work synchronously, then it's simply:

funcArray.forEach(fn => fn());

or

for (const fn of funcArray) {
    fn();
}

or if the result of one function should be passed to the next, you can use reduce:

const finalResult = funcArray.reduce((previousResult, fn) => fn(previousResult), undefined);

...where undefined is the value to pass to func1.

If they're asynchronous

If they don't do their work synchronously, you'll need to provide them a way to notify their caller that they've completed their work. Promises are a good, standard way to do that, but you could use simple callbacks instead.

If you make them return promises, for instance, you can use the old promise reduce trick:

funcArray.reduce((p, fn) => {
    return p.then(() => {
        fn();
    });
}, Promise.resolve());

or if the result of one function should be passed to the next:

funcArray.reduce((p, fn) => {
    return p.then(fn);
}, Promise.resolve());

You can provide an argument to Promise.resolve to set the value to pass to func1 (without one, it'll receive undefined).

Solution 2:[2]

You can bind to the function the index where it is in the array so you can use this index to get and call the next function:

var funcArray = [func1, func2];
var boundFuncArray = funcArray.map((f, i) => f.bind(null, i));

boundFuncArray[0](); 

function func1(nextFunctionIndex) {
    console.log('func1 called');
    // Execute next function:
    var nextFunc = boundFuncArray[nextFunctionIndex + 1];
    nextFunc && nextFunc();
}

function func2(nextFunctionIndex) {
    console.log('func2 called');
    // Execute next function:
    var nextFunc = boundFuncArray[nextFunctionIndex + 1];
    nextFunc && nextFunc();
}

As T.J Crowder stated in the comment below, you can also bind the next function to the current one:

var funcArray = [func1, func2];
var boundFuncArray= funcArray.map((f, i, arr) => f.bind(null, arr[i + 1]));

boundFuncArray[0](); 

function func1(nextFunc) {
    console.log('func1 called');
    // Execute next function:
    nextFunc && nextFunc();
}

function func2(nextFunc ) {
    console.log('func2 called');
    // Execute next function:
    nextFunc && nextFunc();
}

Solution 3:[3]

You can get the current function's name with arguments.callee.name, loop through the array of functions, and call the next function:

funcArray = [func1, func2, func3];

// Only func1() and func2() will be documented since the others have repeating code

function func1() {
    // show the current function name
    console.log(arguments.callee.name);

    // loop the array of functions
    for(var i = 0; i < funcArray.length; ++i)
    {
        // when the current array item is our current function name and
        // another function exists after this then call it and break
        if(funcArray[i] === arguments.callee && funcArray[i+1])
        {
            funcArray[i+1]();
            break;
        }
    }
}

function func2() {
    console.log(arguments.callee.name);
    
    // some logic which switches our next function to be func4()
    funcArray[2] = func4;
    
    for(var i = 0; i < funcArray.length; ++i)
    {
        if(funcArray[i] === arguments.callee && funcArray[i+1])
        {
            funcArray[i+1]();
            break;
        }
    }
}

function func3() {
    console.log(arguments.callee.name);
    for(var i = 0; i < funcArray.length; ++i)
    {
        if(funcArray[i] === arguments.callee && funcArray[i+1])
        {
            funcArray[i+1]();
            break;
        }
    }
}

function func4() {
    console.log(arguments.callee.name);
    for(var i = 0; i < funcArray.length; ++i)
    {
        if(funcArray[i] === arguments.callee && funcArray[i+1])
        {
            funcArray[i+1]();
            break;
        }
    }
}

// call the first function
funcArray[0]();

Output:

func1
func2
func4

Solution 4:[4]

I dont know if your functions require certain parameters but this is the first thing that came to my mind.

var functArray = [
  function() {
    console.log("function1 executed");
  },
   function() {
    console.log("function2 executed");
  },
    function() {
    console.log("function3 executed");
  },
    function() {
    console.log("function4 executed");
  }];
  
  functArray.forEach(function(x){
    x();
  });

Solution 5:[5]

The accepted answer and other comments did help me, but the way I implemented it is as follows:

//The functions are defined as variables. 
//They do not get hoisted, so must be defined first.
func1 = function (arg1, arg2) {
  //Code to do whatever...
  ...
  //Execute the next function. 
  //The name of the function is returned by executing nextFunc()
  global[nextFunc()](arg1, arg2, arg3);

}
func2 = function (arg1)  { //Note different type of args
  ...
}

//Note that this is an array of strings representing function names.
funcArray = ["func1", "func2", "func3",...]

//Start the execution...
func1(arg1, arg2);

function nextFunc() {
  var currentFuncName = nextFunc.caller.name;  
  var index = funcArray.indexOf(currentFuncName);
  if (index < funcArray.length)
    return funcArray[index+1];
}

The sequence of functions to be executed is easily managed through the array funcArray. The number or type of arguments is not fixed for each function. Additionally, the functions control if they should stop the chain or continue with the next function.

It is very simple to understand requiring basic Javascript skills. No overheads of using Promises.

"global" gets replaced by "window" for browser. This is a Node.js implementation. The use of function names in the array will, however, break if you minify the JS code. As I am going to use it on the server, I do not expect to minify it.

Solution 6:[6]

You can do it in this way with promise.all if your functions to be executed in parallel.

let toBeExecutedList = [];
toBeExecutedList.push(() => this.addTwoNumber(2, 3));
toBeExecutedList.push(()=>this.square(2));

And Then wherever you want to use them, do it like this:

const resultArr = await Promise.all([
      toBeExecutedList.map(func => func()),
    ]);

Solution 7:[7]

I have solved it this way:

// Adding next options to array
function addNext(array) {
   array.last = 1
   Object.defineProperty(array, 'next', {get: 
      function() {
         if(this.last < this.length) {
            this.last++
            return this[this.last-1]
         } else {
            this.last = 1
            return () => {}
         }
      }
   });
}

// The functions for array (has to be function and not arrow function)
function first(param) {
   console.log('first',param)
   return this.next(param)
}
function second(param) {
   console.log('second',param)
   return this.next(param)
}
function third(param) {
   console.log('third',param)
   return this.next(param)
}

// The array
let fns = [first,second,third]

// Adding next option to array
addNext(fns)

// Run first function from array 
fns[0]('test')

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3
Solution 4 Fabian Gonzalez
Solution 5
Solution 6 pride
Solution 7 Alex Sorkin