Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert an array of functions into nested function calls

Tags:

javascript

I am given an array of functions of length N, where every function has an asynchronous call and accepts a callback argument. The callback of function[x] is function[x+1], and function[N-1] has no callback. Here's an example of what I might be given:

function func1(callback){
  $.ajax({
    //Ajax settings
  }).done(function(){
    console.log('foo1');
    if(callback) callback();
  });
}

function func2(callback){
  $.ajax({
    //Ajax settings
  }).done(function(){
    console.log('foo2');
    if(callback) callback();
  });
}

function func3(callback){
  $.ajax({
    //Ajax settings
  }).done(function(){
    console.log('foo3');
    if(callback) callback();
  });
}

var funcs = [func1, func2, func3];

Am I able to build the following nested calls for the example without just building a string and calling eval():

func1(function(){
  func2(function(){
    func3()
  })
})

/* 
  Output:
  foo1
  foo2
  foo3
*/

Edit: They have to be nested because I need them to run synchronously. For-loops or foreach() create race conditions.

like image 601
Greg Mc Avatar asked Jan 13 '20 17:01

Greg Mc


3 Answers

Make a new function, which chains all the functions in the array:

funcx = funcs.reduceRight((f2, f1) => x => f1(f2(x)));

Then call this function:

funcx(); 

Of course, you could do it also in on step, but it might be a bit confusing:

funcs.reduceRight((f2, f1) => x => f1(f2(x)))();

If you want to be prepared for an empty array of functions, you can do it like this:

funcs.reduceRight((f2, f1) => x => f1(f2(x)), x => 0)();
like image 192
Donat Avatar answered Oct 16 '22 03:10

Donat


Creating Promise can make the solution easy. Please follow the sample.

const delay =  (v) => {
  return new Promise(r => {
    setTimeout(x => r(v), 10000 * Math.random())
  })
}
async function  dummyAjax() {
  const data = await delay(Math.random() * 10)
  return data
}
async function  func1() {
  return dummyAjax()
}
async function  func2() {
  return dummyAjax()
}
async function  func3() {
  return dummyAjax()
}
console.time("PROMISE: TOTAL_TIME")
Promise.all([func1(), func2(), func3()])
    .then((responses) => {
        responses.forEach(console.log)
        console.timeEnd("PROMISE: TOTAL_TIME")
    })

I have created a series of the solution on all types of async ways to addressing the issue. Please check my gist page.

https://gist.github.com/deepakshrma/1a899bf49ccb77d3b5be771907b32e4c

like image 1
xdeepakv Avatar answered Oct 16 '22 04:10

xdeepakv


If all of your functions returned the promise, then your solution is easy. First, add a return statement to each of your functions:

function func1(callback){
  return $.ajax(...);
}

Then, you could just do this:

func1()
.then(func2)
.then(func3)
.then(result => console.log('DONE!', result))
.catch(err => console.log('OH NO!', err));

If you want to keep the array, then you can do this:

var funcs = [func1, func2, func3];
funcs.reduce((promise, func, i) => {
  if(i === 0) return promise();
  return promise.then(func);
})
.then(result => console.log('DONE!', result))
.catch(err => console.log('OH NO!', err));
like image 1
Ryan Wheale Avatar answered Oct 16 '22 03:10

Ryan Wheale