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
Related
I have a Node.js web application in which I make some 3rd party API calls with, creating some really long request/response times. This causes my Heroku to timeout ~50% of the time, and according to Heroku docs there's no way to change this hard limit. They recommend using background processes to scale, and this seemed like a good fit to the problem (they provide examples and recommend using https://optimalbits.github.io/bull/).
I'm following the simple example provided by Heroku here https://github.com/heroku-examples/node-workers-example), however for my sake this example is a bit too simple.
I have a filed named worker.js which will be my worker. There is a function start() that instantiates the queue, and tells the queue how to process jobs. My issue is that I will be adding jobs to the queue via my API endpoints, and I would like each job to execute specific functions in order to complete their task.
Here is the start() function:
function start(){
let workQueue = new Queue('work', REDIS_URL);
workQueue.process(maxJobsPerWorker, async (job,data) => {
return {message: "Finished"};
});
What I would like to do is to execute different functions in the logic block instead of returning a finished message. For example, I'd like to do something like this:
function start(){
let workQueue = new Queue('work', REDIS_URL);
workQueue.process(maxJobsPerWorker, async (job,data) => {
return someFunction();
});
Again, that would be straight-forward enough if I needed to run the same function every time, but I will be running several different functions that use different inputs. So someFunction is a variable and not a constant.
I was considering passing the name of the function to execute to the job directly, something like this:
let job = await workQueue.add({ callback: functionName });
However I can't really tell if this works or not... How can I get this up and running?
I don't think the start function is necessary. You can just define your functions like this async function myFunction(){ //Logic goes here } and then when you need that call you just execute them using await myFunction() or of it returns something you can put it in a variable. But the function that you want to execute needs to contain the async word for await to work.
Also return works the same way as await. So you could let's say you have a getter method you can say something like async function findWorker(){ return database.find({ workerId }); };.
I am currently building a signboard system that displays timetable information in a consistent format for various locations.
The idea is that each location has its own lightweight page with a small amount of variables that define the location specific parameters and then call the appropriate functions, in order, from a single external .js file.
My page is working fine with functions explicitly chained together, like so:
function one (){
//do the thing
two();
}
function two (){
//do the next thing
three();
}
function three (){
//do the last thing
}
What I am trying to do is separate the functions so that I can call them from a list in each individual page which will let me substitute different versions of certain functions as required in the different locations. Something like this:
function callList(){
one();
//wait for one to finish
two();
//wait for two to finish
three();
}
I have spent a lot of time reading about asynchronous functions, callbacks, promises etc. but the solutions that have been offered still seem to deal more with chaining functions together explicitly and passing a single variable as proof the function has finished, such as this (well written) example:
https://flaviocopes.com/javascript-async-await/
Part of my difficulty in figuring out the right solution is that my functions are quite varied in their purpose. Many of my functions don't produce variables at all and the ones that do (with the exception of a single ajax call) produce large sets of global parameters that don't need to be explicitly passed to the next function. Most, in fact, focus on rendering and manipulating svg and text in various ways, and due to the nature of the data displayed many rely heavily on loops.
As with most javascript problems I encounter I am sure that it is merely a gap in my understanding, but I feel like I am just reading the same articles over and over again and getting nowhere. I really need someone more knowledgeable to give me a nudge in the right direction.
Thanks.
Functions are first-class citizens in Javascript, so you can just throw them into an array and then loop through and call them.
var functionsToCall = [
one,
two,
three
];
// Call them (use your looping method of choice)
for (var i = 0; i < functionsToCall.Length; i++) {
functionsToCall[i]();
}
If your functions are synchronous and are not returning anything that you need, that's basically all you need. If your functions are async, then you might need something more like await functionsToCall[i](); or a setup using promises/callbacks instead.
If you need callbacks to tell you when a function has completed, you can use a small state manager/function to handle that (or you can use async/awaits if your environment will support them - they're cleaner to write! :) ).
Something like...
// A sample async function - you pass the callback to it.
function one(callback) {
// Do some async work, like AJAX...
// Let the callback know when I'm finished (whether I have a value to return or not.
callback();
}
// Simple state management - wrap these up with nicer code and handle errors and whatnot.
var funcIndex = 0;
function callNext() {
if (funcIndex < functionsToCall.Length) {
functionsToCall[funcIndex](callNext);
funcIndex += 1;
}
}
// To start things off:
function callAllFunctions() {
funcIndex = 0;
callNext();
}
If you need to have more granular control over the function calling, you can put custom objects into the array instead of just the functions themselves and change the behavior based on that.
For example:
var functionsToCall = [
{ func: one, isAsync: true },
{ func: two, isAsync: false }
];
Anyway, just some possibilities. It will really depend on exactly what you need for your particular situation!
use await
or
use promises
or
you need function1 execution complete handler will execute next one
I know this question has been asked tens if not hundreds of times, but I turned google purple and still can't find an answer that suits my case.
I have a set of three functions that I need to call one from within the other, and need each function to finish before the one that calls it continues. Currently what happens is that a function would call another one, and then continue before the function it called finished.
Most of what I have seen says to use callback functions, my problem is that my inner most function is taken from a library, thus I can not adapt it to accept a callback function as a parameter. I also saw things on timeouts, but I do not want to force my code to wait any longer than it has to, I just want it to continue once the function it calls finished.
I just want everything to work synchronously, like I am used to from any other language.
To illustrate my current code, I am using three.js and this is (basically) what I have:
firstfunction(){
secondFunction();
}
secondFunction(){
var loader = new THREE.JSONLoader(); //loader is an object from three.js library
//loader.load is a three.js function that calls thirdFunction that I made. I can not make loader.load send a callback function to thirdFunction, as thirdFunction takes its arguments from three.js library
loader.load(url, thirdFunction);
}
thirdFunction(){ //this is a function that gets called from loader.load
//do stuff
}
I feel like I am missing something very trivial, but as I said I can't find anything online that fits my needs.
Any help would be greatly appreciated
Even if some libraries and apis allow you to do things synchronously, this is not how javascript should work. Even if you're used to this in other languages, javascript is different.
The answer to your question probably is 'this is not possible'.
Try to lean javascript the correct way, instead of making it behave like other languages.
However, there's some hope. Once you understand fully how callback works, and structure your code around that, you might realize that Promises is a better pattern that callbacks. Promises still need a sort of call-back and are still asynchronous, but it's easier to make your code easier to read.
Then once you fully understand promises, you might be able to use async and await. New browsers support this. These 2 keywords make a lot of your code 'look' synchronous like you're used to, but it's still asynchronous. It's pretty great.
Edit
I wanna address the follow sentence more directly:
//loader.load is a three.js function that calls thirdFunction that I made. I can not make loader.load send a callback function to thirdFunction, as thirdFunction takes its arguments from three.js library
Do you really need to send that third function another callback? What kind of callback is this? Is it just another function it should always call?
You can just call another function normally from your ThirdFunction. If you need it to be variable, you can probably just assign that function to a variable. Functions can access variables from their parent scope. For example:
var callback = '..'; // assuming this a callback.
thirdFunction(){
callback();
}
If only your second function knows what the callback should be, you might need to structure it like this:
secondFunction(){
var loader = new THREE.JSONLoader(); //loader is an object from three.js library
var callback = '...';
loader.load(url, function() {
thirdFunction(callback);
});
}
I have three functions that all work with data from a global object. This global object gets filled with data from a local array in one function and with data from an ajax request with the second function. The third function relies on the data in the object, so the ajax request must have been completed.
I believe I am misunderstanding callbacks. Here's what I do:
var currentCharacter = {}
// this function gets the local data and then calls the second function
function loadData(getMarvelData) {
// do things to fill currentCharacter
getMarvelData(); // this is the callback to the next function (no ?)
}
// this function performs the ajax request, then calls the third function
function getMarvelData(getGoogleMap) {
// do ajax request and add stuff to currentCharacter
getGoogleMap(); // this is the callback to the final function (no ?)
}
function getGoogleMap() {
// do Google Map related stuff with data from currentCharacter
}
I thought setting a function as an argument of another function and then executing it would make the function dependent on the other before it continues. Clearly I still misunderstand callbacks after trying to make it work for a week now. As it is, the getMarvelData function doesn't even get called because I never see the alert popup and the currentCharacter object only has data from the loadData function.
Could somebody show the correct approach for my code, or if my approach of making these three functions is even the right one for this scenario.
The full repository is available at: https://github.com/ChaMbuna/Marvel-Map
V0.9 was actually working but the ajax call was set to run synchronous (it still is btw) Since then I've been overhauling my code to make it work asynchronously and to remove all jQuery (this is a Udacity project and removing jQuery was suggested by an instructor).
Appreciate the help
I have not enough reputation to put a comment, but a wild guess, you should remove the argument in loadData&getMarvelData or actually pass a function in calls to those function.
You have to pass the parameters correctly.
Try this:
var currentCharacter = {}
loadData(getMarvelData, getGoogleMap);
function loadData(f1, f2) {
// do sth.
f1(f2);
}
function getMarvelData(f2) {
// do sth.
f2();
}
function getGoogleMap() {
// do sth.
}
I havn't tested it, but it should work.
I'm trying to figure the best way to get my functions executing in the correct order.
I have 3 functions
function 1 - squirts OPTIONs into a SELECT via JSON and marks them as selected
function 2 - squirts OPTIONS into a 2nd SELECT and marks them as selected
function 3 - gets the values from the above SELECTs along with some additional INPUT values, does an AJAX GET resulting in JSON data, which is read and populates a table.
With JQuery Onload, I execute:
function1();
function2();
function3();
I'm finding function3 is executing before the SELECTs have been populated with OPTIONS and hence the table has no results, because the values sent in the GET were blank.
I know this is probably a very simple problem and that there are probably a dozen ways to accomplish this, but basically I need the best way to code this so that function3 only runs if function1 and 2 are complete.
I've come into Javascript via the back door having learnt the basics of JQuery first!
Thanks for your assistance.
Javascript executes synchronously, which means that function3 must wait for function2 to complete, which must wait for function1 to complete before executing.
The exception is when you run code that is asynchronous, like a setTimeout, setInterval or an asynchronous AJAX request.
Any subsequent code that relies on the completion of such asynchronous code needs to be called in such a manner that it doesn't execute until the asynchronous code has completed.
In the case of the setTimeout, you could just place the next function call at the end of the function you're passing to the setTimeout.
In the case of an AJAX call, you can place the next function call in a callback that fires upon a completed request.
If you don't want the execution of the subsequent function to occur every time, you can modify your functions to accept a function argument that gets called at the end of the asynchronous code.
Something like:
function function1( fn ) {
setTimeout(function() {
// your code
// Call the function parameter if it exists
if( fn ) {
fn();
}
}, 200);
}
function function2() {
// some code that must wait for function1
}
onload:
// Call function1 and pass function2 as an argument
function1( function2 );
// ...or call function1 without the argument
function1();
// ...or call function2 independently of function1
function2();
I recommend you use a Promises library. You can hack simple solutions like other answers suggest, but as your application grows, you'll find you are doing more and more of these hacks. Promises are intended to solve these kinds of problems when dealing with asynchronous calls.
The CommonJS project has several Promises proposals which you should check out. Here is a question I asked on SO about Promises a while back with links to other solutions. Learn more about Promises in this Douglas Crockford video. The whole video is good, but skip to just past half way for promises.
I'm using the FuturesJS library currently as it suits my needs. But there are advantages to other implementations as well. It allows you to do sequences very easily:
// Initialize Application
Futures.sequence(function (next) {
// First load the UI description document
loadUI(next); // next() is called inside loadUI
})
.then(function(next) {
// Then load all templates specified in the description
loadTemplates(next); // next() is called inside loadTemplates
})
.then(function(next) {
// Then initialize all templates specified in the description
initTemplates();
});
Even more powerful is when you need to join async events together and do another action when all of the other async events have completed. Here's an example (untested) that will load a bunch of HTML files and then perform an action only once ALL of them have completed loading:
var path = "/templates/",
templates = ["one.html","two.html","three.html"],
promises = [];
$.each(templates, function(i,name) {
promises[i] = Futures.promise();
var $container = $("<div>");
$container.load(path+name, function(response,status,xhr) {
promises[i].fullfill();
}
});
Futures.join(promises, {timeout: 10000}) // Fail if promises not completed in 10 seconds
.when(function(p_arr) {
console.log("All templates loaded");
})
.fail(function(p_arr) {
console.log("Error loading templates");
});
This might be overkill for your application. But if the application is growing in complexity, using promises will help you in the long run.
I hope this helps!
invoke function2 inside of function1 and function3 inside of function2.
It's not clear why f1 and f2 are executing before f3.
Also, are you using the preferred $(document).ready() or some variation of onload?
It might be helpful if you provide a reproducible test case.
fun3() will only run after both are ready. It might run twice. You can fix this with a lock inside fun3() you would need a Singleton to guarantee it works correctly.
var select1ready = false, select2ready = false;
fun1()
{
// do stuff
select1ready = true;
fun3();
}
fun2()
{
// do stuff
select2ready = true;
fun3();
}
fun3()
{
if (select1ready && select2ready)
{
}
}
fun1();
fun2();