7

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.

5
  • It's an array, so... forEach? funcs.forEach(f => f()); Commented Jan 13, 2020 at 17:47
  • @LukStorms That would create N non-nested calls. The functions themselves are asynchronous, but I need to call the functions in a way that's synchronous to eliminate race conditions. Commented Jan 13, 2020 at 17:50
  • Ah ok, connecting the async's. That's a bit more tricky. You might want to make that goal clear in your question. Commented Jan 13, 2020 at 17:54
  • 1
    Use Promise.all, it is not the straight answer to your question, but it should fulfill your use case better Commented Jan 13, 2020 at 18:21
  • @quirimmo I have to support IE. I didn't say that in the question because I figured supporting IE would be too specific to my use case and didn't want to rule out modern JS APIs, so +1 Commented Jan 13, 2020 at 18:39

3 Answers 3

5

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)();
Sign up to request clarification or add additional context in comments.

3 Comments

He wants output to be 'output1', then 'output2', then 'output3'. So I think you've got to switch your nesting order. Otherwise, I've didn't know about reduceRight, so thanks for that.
@pwilcox, yes, thanks for the hint. I had it first with reduceRight, but then I erreanously changed it to reduce. I have reversed my change now.
This is awesome! Accepted answer because it has the best cross browser compatibility (works in IE9+) and accomplishes exactly what I wanted to do. Thank you!
1

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

Comments

1

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));

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.