简体   繁体   中英

chaining javascript functions

I searched this topic on SO for more than an hour and could not find a case like mine.

I have a web app that I developed on Ruby on Rails. It is based on a form with multiple fields and buttons to execute actions.

These are examples (my real app uses more functions and more combinations of actions): When Field1 is changed, I want to execute JS functions function1(), then function2(), then function3(). When Field2 is changed, I want to execute JS functions function2(), then function3(). When Field3 is changed, I want to execute JS function function3().

All these JS functions call specific actions defined in my controller, and they update the form using AJAX. I'd like function2() to start executing only after function1() has finished updating the form, otherwise the results will be wrong.

Because controller methods that are called by these functions send API calls to many other websites, like Google geocode or Google timezone, their response time is unpredictable, and could be anywhere from a few 100's of ms to a few seconds.

I tried this:

onchange="function1();function2();function3()"

but that did not work because function2() started executing before function1() has finished the updates via AJAX, and the same issue for function3() with regard to function2().

I also tried:

onchange="function1();setTimeout(function(){function2();setTimeout(function(){function3()},FUNCTION2_DELAY)},FUNCTION1_DELAY)"

but that did not work either because if FUNCTION1_DELAY is set too long, function2() will not start executing before FUNCTION1_DELAY has expired, whereas function1() might execute much faster than FUNCTION1_DELAY, and if FUNCTION1_DELAY is set too short, function2() will start executing before function1() has finished the updates that are needed for function2() to run. The same thing for function3() with regard to function2().

My question is : is there a smarter way to make function2() start executing only when function1() has finished updating the form via AJAX, and the same for function3() with regard to function2()? I know I could create new controller methods where I merge these functions, like merging function1(), function2() and function3() into new_function1(), ..., etc., but that makes my code contain repetitive actions and will ultimately make it harder to maintain. So I'd rather look for a smarter way to handle this issue.

you need to use Promises.

Basically it works like that:

var MyFunction1 = new Promise(
    function() {
        //do stuff here
    }
);

MyFunction1.then(Myfunction2 ...).then(Myfunction3 ...;

https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise http://api.jquery.com/promise/

setTimeout is asynchronous, so that won't work. You could call function 2 at the end of function 1, but still inside the function. Similarly for function 3:

function one(){
  ...logic...
  function two();
}

function two(){
  ...logic...
  function three();
}

function three(){
  ...logic...
}

Alternatively, you could create some sort of queue of functions, and only move to the next when the current one is finished:

var functionQueue = [
  one,
  two,
  three
];

function next(){
  var nextFn = functionQueue.shift();
  if( nextFn ){
    nextFn();
  }
}

function one(){
  ...logic...
  next();
}
// same for two & three

You can also use promises, which are quite powerful and make the code clearer to read, but may be overkill for this problem

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM