Call a function asynchronous in a synchronous code-base - javascript

I have a project consisting out of thousands of lines of Javascript code. It contains a lot of complex calculations which are divided into different functions. One function calls three or four other functions, of which one calls another one of those four functions,... Just to say, it has a complex structure.
Some functions also need to do calculations on the DOM. E.g, One functions adds an element to the DOM, and another one needs to do a calculation using the coordinates of that element in the DOM.
However, when the user visits the page, all of this is executed by one function called - in this example - bigFunction.
The problem is that bigFunction starts a long series of complex calculations and function calls resulting in stalling the page loading, and making the page irresponsive until bigFunction is finished.
I already tried simple solutions like defering my .js-file, using $(document).ready(function() { bigFunction(); }), $(window).on('load', function() { bigFunction(); }). None of that changed anything. I tried converting my code to async code but that was crazy work.
I am actually just wondering if there is a way to call bigFunction asynchronous somehow but without converting all of my code to async code and making all my functions async, is that possible?
Some test-code:
// Executing big function
bigFunction();
// "HTML"
console.log("[+] HTML Loaded!!"); // -> Must load async with bigFunction running
// This is a function I execute on a page and which stops the page from
// loading until it's finished. How do I async this?
async function bigFunction() {
console.log("[+] bigFunction started");
if (functionToReturnTrueOrFalseAfterALongCalculation()) {
console.log("WOW!");
}
console.log("[+] bigFunction finished")
}
// Ignore logic in this function... It just returns true of false randomly after 3,5 secs
function functionToReturnTrueOrFalseAfterALongCalculation() {
console.log("[+] functionToReturnTrueOrFalseAfterALongCalculation started");
let to_return = false;
// Give back random value
let random_number = Math.floor(Math.random() * 100) + 1;
if (random_number < 50) {
to_return = true;
}
sleep(3500);
// Just to show that functions are nested deep in each other
doRandomStuff();
console.log("[+] functionToReturnTrueOrFalseAfterALongCalculation ended");
return to_return;
}
// Just another slow function
function doRandomStuff() {
console.log("[+] doRandomStuff started");
sleep(1000);
console.log("[+] doRandomStuff ended");
}
// Ignore
function sleep(milliseconds) {
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++) {
if ((new Date().getTime() - start) > milliseconds){
break;
}
}
}
HTML should load faster...

I am actually just wondering if there is a way to call bigFunction asynchronous somehow but without converting all of my code to async code and making all my functions async, is that possible?
Fundamentally, not really. In order for the page to remain responsive, your JavaScript code must yield control back to the browser regularly. If you have some very expensive JS that blocks for 0.5 seconds, the page will appear to the user to be unresponsive for those 0.5 seconds. (0.5 seconds of blocking is not good. 0.05 seconds of blocking every 0.1 seconds could be better, for example.)
To have the site remain responsive, all of your functions that invoke sleep (the expensive calculations) need to be refactored to stagger up their jobs. Pure JavaScript jobs can be offloaded to a service worker, thereby freeing up resources to keep the active tab responsive, but jobs that require DOM manipulation (some of which you have) can't be offloaded to a service worker.
Without seeing what exactly the expensive functions are doing, more details are difficult, but for what you want, in the end you'll probably need to end up with code that looks something like
async function bigFunction() {
console.log("[+] bigFunction started");
// the below function does not block for long periods of time,
// but only for short periods of time, staggered up
const result = await functionToReturnTrueOrFalseAfterALongCalculation();
if (result) {
console.log("WOW!");
}
console.log("[+] bigFunction finished")
}
The code that comes after the execution of bigFunction would absolutely have to be able to handle the asynchronous execution flow - if you had
doSomething();
bigFunction();
finishDoingStuff();
you would have to refactor to something like
doSomething();
await bigFunction();
finishDoingStuff();
or to
doSomething();
bigFunction(finishDoingStuff);
where bigFunction calls finishDoingStuff once its expensive tasks are complete.

Related

Javascript for-loop async File execution

In the next code, I want to process several files at the same time without wait to the end of each other. For this reason, I first read the files (array) and then the callback is called to process an element of this array instance.
I have found a problem into this javascript code, exactly in a async for-loop, where this process is executed as a sync code instead of async.
var array = ['string1','string2','string3','string4'];
function processArray (arrayString,callback){
//Read file Example.csv thought sync way
try{
var ifs = new InputFileStream('Example.csv','utf8');
table = ifs.read(0);
ifs.close();
}catch(err){
console.log(err.stack);
}
callback(arrayString, table);
}
//Async for
for (var i=0; i<array.length; i++) {
processArray(array[i], function(arrayString, table){
//Here process the file values thought async way
console.log('processed_'+i);
});
}
You could put the call back in a setTimeout with a delay of 1ms. That will run it in the next block of execution and your loop will continue on.
e.g. use this:
setTimeout(function() { callback(arrayString, table); }, 1);
instead of this:
callback(arrayString, table);
An alternative to this is to run the callback on a separate thread using Web Workers. I don't think it would appropiate to provide a long answer describing how to do multi threaded JavaScript here so I'll just leave the link to the docs. https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers
where this process is executed as a sync code instead of async
I've seen that you just have find out the answers of your question, remember that JavaScript is single thread.
So, for that when you execute operations that require full use of CPU like for..loops, while, etc; you just will get your code running synchronous and not only that,
You will get your web page freeze if they are huge loops
Let me give you an example, this is a while loop that will run for 6 seconds, look how you cannot do anything in stackoverflow.
function blocker (ms) {
console.log('You cannot do anything')
var now = new Date().getTime();
while(true) {
if (new Date().getTime() > now +ms)
return;
}
}
blocker(6000) //This stop your entire web page for 6 seconds
If you really want to achieve running blocking code in the background read about Web Workers or you just can use a small library I wrote, that allow you to execute a blocking CPU function in the background, I called it GenericWebWorker

Javascript sleep() function executed early

I am trying to figure out why in my Code section, this.sleep(5000) seems to be getting called before my draw function, because it doesn't get drawn to the canvas until after sleep is done. any insights on why this isn't working the way I want it to?
Sleep function:
sleep: function(milliseconds) {
setTimeout(function(){
var start = new Date().getTime();
while ((new Date().getTime() - start) < milliseconds){
// Do nothing
}
},0);
},
Code:
var g = new Graph(this.diagram);
g.DrawPolygons(ctx,"blue");
this.sleep(5000);
Short answer
Don't do it this way. Even if you get it to work, it will be inconsistent, will cause you many problems, and is almost globally considered bad practice.
Long answer
JavaScript runtimes are almost always designed to be asynchronous. Your while loop is intended to make everything... wait. You cannot (or at least shouldn't) do that in most JavaScript environments.
Instead, schedule events/functions to be executed some number of ms in the future. This is what setTimeout is for. This removes the need for a sleep function.
Here's what your code might look like after the changes described above are applied:
var g = new Graph(this.diagram);
g.DrawPolygons(ctx, "blue");
setTimeout(function() {
g.DrawPolygons(ctx, "red"); // Or whatever
setTimeout(function() {
g.DrawPolygons(ctx, "yellow"); // Or whatever
// etc..
}, 5000);
}, 5000);
ES2015 update - using promises
To avoid potential deeply nested setTimeouts, you can use this
const sleep ms = new Promise(resolve => setTimeout(resolve,ms));
which is simply a promise that resolves in ms milliseconds. This allows you to keep everything in one block:
var g = new Graph(this.diagram);
g.DrawPolygons(ctx, "blue");
(async () => {
g.DrawPolygons(ctx, "red");
await sleep(5000);
g.DrawPolygons(ctx, "yellow");
await sleep(5000);
// ...
})()
Note two things:
Under the hood, there are still events/callback. It looks like C's or Python's sleep but behave very differently.
You can only use this inside asynchronous functions. See here for more information.
There are several problems with the code you've posted. First off, you should never use a while loop to halt code execution.
Secondly, you're setting a timeout, which allows other code to be executed in the interim (yes, even if the timeout is zero seconds). Remove that and you should be able to pause execution (BUT DON'T DO THIS):
sleep: function(milliseconds) {
var start = new Date().getTime();
while ((new Date().getTime() - start) < milliseconds){
// Do nothing
}
},
However, occupying the JS thread means that other browser operations (redraws, etc) will be halted until your sleep function exits. Just having this code in your JS file is an antipattern, you'd be better off finding a different way to solve your problem. Read up on the XY problem and ask a new question.
In case all you wanted to do was execute some code after a certain interval without blocking everything else, setTimeout is all you need.
sleep: function(ms, funcToExecute) {
setTimeout(funcToExecute, ms);
},
(Though at this point, sleep is redundant)
This is happening because of how JavaScript's setTimeout works. When you do:
setTimeout(function(){}, 0)
You are not actually telling it to run the function after 0ms (the lowest value is actually 4ms, but that's besides the point). You are telling it to run the function in the future. What it actually does is put the function at "the end of the stack". It'll finish running the function that called it, and maybe even run some UI redraws before it runs the timeout.
If this code is ran in a loop, your timeouts will not run at all when you think they will ;)
Also, remember JavaScript is single threaded. One thread runs your code as well as the UI redraws. Doing a while loop that does nothing and waits for 5 seconds will lock up the browser. It will prevent any user interaction and UI redraws. It might even make the OS think the browser crashed. DO NOT DO THIS!
Instead, try setting a timeout to run the next polygon after 5000ms:
var g = new Graph(this.diagram);
g.DrawPolygons(ctx,"blue");
setTimeout(function(){
// Code to run after the "sleep"
// Maybe another shape
g.DrawPolygons(ctx, "red");
}, 5000);

node.js: while loop callback not working as expected

Knowing that while Node.js is working asynchronously, writing something like this:
function sleep() {
var stop = new Date().getTime();
while(new Date().getTime < stop + 15000) {
;
}
}
sleep();
console.log("done");
...would call the sleep(), block the server for the duration of the while loop (15secs) and just THEN print "done" to the console. As far as I understand, this is because Node.js is giving JavaScript only access to the main thread, and therefore this kidn of thing would halt further execution.
So I understand the solution to this is to use callbacks:
function sleep(callback) {
var stop = new Date().getTime();
while(new Date().getTime() < stop + 15000) {
;
}
callback();
}
sleep(function() {
console.log("done sleeping");
});
console.log("DONE");
So I thought this would print 'DONE' and after 15 secs. 'done sleeping', since the sleep() function gets called and is handed a pointer to a callback function. While this function is working (the while loop), the last line would be executed (print 'done'). After 15 seconds, when the sleep() function finishes, it calls the given callback function, which then prints 'done sleeping'.
Apparently I understood something wrong here, because both of the above ways block. Can anybody clarify please?
Thanks in advance,
Slagjoeyoco
Javascript and node.js are single threaded, which means a simple while blocks; no requests/events can be processed until the while block is done. Callbacks don't magically solve this problem, they just help pass custom code to a function. Instead, iterate using process.nextTick, which will give you esentially the same results but leaves space for requests and events to be processed as well, ie, it doesn't block:
function doSleep(callback) {
var stop = new Date().getTime();
process.nextTick(function() {
if(new Date().getTime() < stop + 15000) {
//Done, run callback
if(typeof callback == "function") {
callback();
}
} else {
//Not done, keep looping
process.nextTick(arguments.callee);
}
});
}
doSleep(function() {
console.log("done sleeping");
console.log("DONE");
});
You are calling sleep right away, and the new sleep function blocks. It keeps iterating until the condition is met. You should use setTimeout() to avoid blocking:
setTimeout(function () {
console.log('done sleeping');
}, 15000);
Callbacks aren't the same thing as asynchronicity, they're just helpful when you want to get a... callback... from an asynchronous operation. In your case, the method still executes synchronously; Node doesn't just magically detect that there's a callback and long-running operation, and make it return ahead of time.
The real solution is to use setTimeout instead of a busy loop on another thread.
As already mentioned, asynchronous execution should be achieved by setTimeout() rather than while, because while will freeze in one "execution frame".
Also it seems you have syntax error in your example.
This one works fine: http://jsfiddle.net/6TP76/

Why there is no sleep functionality in javascript when there is setTimeout and setInterval?

Why there no such function in javascript that sets a timeout for its continuation, saves the necessary state (the scope object and the execution point), terminates the script and gives the control back to the browser? After the timeout expires the browser would load back the execution context and continues the script, and we would have a real non browser blocking sleep functionality that would work even if the JS engine is single threaded.
Why there is still no such functionality in javascript? Why do we have to still slice our code into functions and set the timeouts to the next step to achieve the sleep effect?
I think 'sleep'ing is something you do not want in your browser.
First of all it might be not clear what has to happen and how a browser should behave when you actually sleep.
Is the complete Script runtime sleeping? Normally it should because you only have one thread running your code. So what happens if other events oocur during sleep? they would block, and as soon execution continues all blocked events would fire. That will cause an odd behaviour as you might imagine (for instance mouse click events which are fired some time, maybe seconds, after the actual click). Or these events had to be ignored, which will lead to a loss of information.
What will happen to your browser? Shall it wait for sleep if the user clicks a (e.g. close window) button? I think not, but this might actually call javascript code again (unload) which will not be able to be called since program execution is sleeping.
On a second thought sleep is a sign of poor program design. Actually a program/function/you name it has a certain task, which shall be completed as soon as possible. Sometimes you have to wait for a result (for instance you wait for an XHR to complete) and you want to continue program execution meanwhile. In this case you can and should use asynchronous calls. This results in two advantages:
The speed of all scripts is enhanced (no blocking of other scripts due to sleep)
The code is executed exactly when it should and not before or after a certain event (which might lead to other problems like deadlocks if two functions check for the same condition ...)
... which leads to another problem: Imagine two or more pieces of code would call sleep. They would hinder themselves if they try to sleep at the same, maybe unnecessarily. This would cause a lot of trouble when you like to debug, maybe you even have difficulties in ensuring which function sleeps first, because you might control this behavior somehow.
Well I think that it is one of the good parts of Javascript, that sleep does not exist. However it might be interesting how multithreaded javascripts could perform in a browser ;)
javascript is desgined for single process single thread runtime, and browser also puts UI rendering in this thread, so if you sleep the thread, UI rendering such as gif animation and element's event will also be blocked, the browser will be in "not responding" state.
Maybe a combination of setTimeout and yield would work for your needs?
What's the yield keyword in JavaScript?
You could keep local function scope while letting the browser keep going about its work.
Of course that is only in Mozilla at the moment?
Because "sleep()" in JavaScript would make for a potentially horrible user experience, by freezing the web browser and make it unresponsive.
What you want is a combination of yield and Deferreds (from jquery for example).
It's called sometimes pseudoThreads, Light Threading or Green Threads. And you can do exactly what you want with them in javascript > 1.7 . And here is how:
You'll need first to include this code:
$$ = function (generator) {
var d = $.Deferred();
var iter;
var recall = function() {
try {var def = iter.send.apply(iter, arguments);} catch(e) {
if (e instanceof StopIteration) {d.resolve(); return;}
if (e instanceof ReturnValueException) {
d.resolve(e.retval); return
};
throw e;
};
$.when(def).then(recall); // close the loop !
};
return function(arguments) {
iter = generator.apply(generator, arguments);
var def = iter.next(); // init iterator
$.when(def).then(recall); // loop in all yields
return d.promise(); // return a deferred
}
}
ReturnValueException = function (r) {this.retval = r; return this; };
Return = function (retval) {throw new ReturnValueException(retval);};
And of course call jquery code to get the $ JQuery acces (for Deferreds).
Then you'll be able to define once for all a Sleep function:
function Sleep(time) {
var def = $.Deferred();
setTimeout(function() {def.resolve();}, time);
return def.promise();
}
And use it (along with other function that could take sometime):
// Sample function that take 3 seconds to execute
fakeAjaxCall = $$(function () {
yield (Sleep(3000));
Return("AJAX OK");
});
And there's a fully featured demo function:
function log(msg) {$('<div>'+msg+'</div>').appendTo($("#log")); }
demoFunction = $$(function (arg1, arg2) {
var args = [].splice.call(arguments,0);
log("Launched, arguments: " + args.join(", "));
log("before sleep for 3secs...");
yield (Sleep(3000));
log("after sleep for 3secs.");
log("before call of fake AjaxCall...");
ajaxAnswer = yield (fakeAjaxCall());
log("after call of fake AjaxCall, answer:" + ajaxAnswer);
// You cannot use return, You'll have to use this special return
// function to return a value
log("returning 'OK'.");
Return("OK");
log("should not see this.");
});
As you can see, syntax is a little bit different:
Remember:
any function that should have these features should be wrapped in $$(myFunc)
$$ will catch any yielded value from your function and resume it only when
the yielded value has finished to be calculted. If it's not a defered, it'll work
also.
Use 'Return' to return a value.
This will work only with Javascript 1.7 (which is supported in newer firefox version)
It sounds like what you're looking for here is a way to write asynchronous code in a way that looks synchronous. Well, by using Promises and asynchronous functions in the new ECMAscript 7 standard (an upcoming version of JavaScript), you actually can do that:
// First we define our "sleep" function...
function sleep(milliseconds) {
// Immediately return a promise that resolves after the
// specified number of milliseconds.
return new Promise(function(resolve, _) {
setTimeout(resolve, milliseconds);
});
}
// Now, we can use sleep inside functions declared as asynchronous
// in a way that looks like a synchronous sleep.
async function helloAfter(seconds) {
console.log("Sleeping " + seconds + " seconds.");
await sleep(seconds * 1000); // Note the use of await
console.log("Hello, world!");
}
helloAfter(1);
console.log("Script finished executing.");
Output:
Sleeping 1 seconds.
Script finished executing.
Hello, world!
(Try in Babel)
As you may have noticed from the output, this doesn't work quite the same way that sleep does in most languages. Rather than block execution until the sleep time expires, our sleep function immediately returns a Promise object which resolves after the specified number of seconds.
Our helloAfter function is also declared as async, which causes it to behave similarly. Rather than block until its body finishes executing, helloAfter returns a Promise immediately when it is called. This is why "Script finished executing." gets printed before "Hello, world!".
Declaring helloAfter as async also allows the use of the await syntax inside of it. This is where things get interesting. await sleep(seconds * 1000); causes the helloAfter function to wait for the Promise returned by sleep to be resolved before continuing. This is effectively what you were looking for: a seemingly synchronous sleep within the context of the asynchronous helloAfter function. Once the sleep resolves, helloAfter continues executing, printing "Hello, world!" and then resolving its own Promise.
For more information on async/await, check out the draft of the async functions standard for ES7.

How should I call 3 functions in order to execute them one after the other?

If I need call this functions one after other,
$('#art1').animate({'width':'1000px'},1000);
$('#art2').animate({'width':'1000px'},1000);
$('#art3').animate({'width':'1000px'},1000);
I know in jQuery I could do something like:
$('#art1').animate({'width':'1000px'},1000,'linear',function(){
$('#art2').animate({'width':'1000px'},1000,'linear',function(){
$('#art3').animate({'width':'1000px'},1000);
});
});
But, let's assume that I'm not using jQuery and I want to call:
some_3secs_function(some_value);
some_5secs_function(some_value);
some_8secs_function(some_value);
How I should call this functions in order to execute some_3secs_function, and AFTER that call ends, then execute some_5secs_function and AFTER that call ends, then call some_8secs_function?
UPDATE:
This still not working:
(function(callback){
$('#art1').animate({'width':'1000px'},1000);
callback();
})((function(callback2){
$('#art2').animate({'width':'1000px'},1000);
callback2();
})(function(){
$('#art3').animate({'width':'1000px'},1000);
}));
Three animations start at same time
Where is my mistake?
In Javascript, there are synchronous and asynchronous functions.
Synchronous Functions
Most functions in Javascript are synchronous. If you were to call several synchronous functions in a row
doSomething();
doSomethingElse();
doSomethingUsefulThisTime();
they will execute in order. doSomethingElse will not start until doSomething has completed. doSomethingUsefulThisTime, in turn, will not start until doSomethingElse has completed.
Asynchronous Functions
Asynchronous function, however, will not wait for each other. Let us look at the same code sample we had above, this time assuming that the functions are asynchronous
doSomething();
doSomethingElse();
doSomethingUsefulThisTime();
The functions will be initialized in order, but they will all execute roughly at the same time. You can't consistently predict which one will finish first: the one that happens to take the shortest amount of time to execute will finish first.
But sometimes, you want functions that are asynchronous to execute in order, and sometimes you want functions that are synchronous to execute asynchronously. Fortunately, this is possible with callbacks and timeouts, respectively.
Callbacks
Let's assume that we have three asynchronous functions that we want to execute in order, some_3secs_function, some_5secs_function, and some_8secs_function.
Since functions can be passed as arguments in Javascript, you can pass a function as a callback to execute after the function has completed.
If we create the functions like this
function some_3secs_function(value, callback){
//do stuff
callback();
}
then you can call then in order, like this:
some_3secs_function(some_value, function() {
some_5secs_function(other_value, function() {
some_8secs_function(third_value, function() {
//All three functions have completed, in order.
});
});
});
Timeouts
In Javascript, you can tell a function to execute after a certain timeout (in milliseconds). This can, in effect, make synchronous functions behave asynchronously.
If we have three synchronous functions, we can execute them asynchronously using the setTimeout function.
setTimeout(doSomething, 10);
setTimeout(doSomethingElse, 10);
setTimeout(doSomethingUsefulThisTime, 10);
This is, however, a bit ugly and violates the DRY principle[wikipedia]. We could clean this up a bit by creating a function that accepts an array of functions and a timeout.
function executeAsynchronously(functions, timeout) {
for(var i = 0; i < functions.length; i++) {
setTimeout(functions[i], timeout);
}
}
This can be called like so:
executeAsynchronously(
[doSomething, doSomethingElse, doSomethingUsefulThisTime], 10);
In summary, if you have asynchronous functions that you want to execute syncronously, use callbacks, and if you have synchronous functions that you want to execute asynchronously, use timeouts.
This answer uses promises, a JavaScript feature of the ECMAScript 6 standard. If your target platform does not support promises, polyfill it with PromiseJs.
Look at my answer here Wait till a Function with animations is finished until running another Function if you want to use jQuery animations.
Here is what your code would look like with ES6 Promises and jQuery animations.
Promise.resolve($('#art1').animate({ 'width': '1000px' }, 1000).promise()).then(function(){
return Promise.resolve($('#art2').animate({ 'width': '1000px' }, 1000).promise());
}).then(function(){
return Promise.resolve($('#art3').animate({ 'width': '1000px' }, 1000).promise());
});
Normal methods can also be wrapped in Promises.
new Promise(function(fulfill, reject){
//do something for 5 seconds
fulfill(result);
}).then(function(result){
return new Promise(function(fulfill, reject){
//do something for 5 seconds
fulfill(result);
});
}).then(function(result){
return new Promise(function(fulfill, reject){
//do something for 8 seconds
fulfill(result);
});
}).then(function(result){
//do something with the result
});
The then method is executed as soon as the Promise finished. Normally, the return value of the function passed to then is passed to the next one as result.
But if a Promise is returned, the next then function waits until the Promise finished executing and receives the results of it (the value that is passed to fulfill).
It sounds like you're not fully appreciating the difference between synchronous and asynchronous function execution.
The code you provided in your update immediately executes each of your callback functions, which in turn immediately start an animation. The animations, however, execute asyncronously. It works like this:
Perform a step in the animation
Call setTimeout with a function containing the next animation step and a delay
Some time passes
The callback given to setTimeout executes
Go back to step 1
This continues until the last step in the animation completes. In the meantime, your synchronous functions have long ago completed. In other words, your call to the animate function doesn't really take 3 seconds. The effect is simulated with delays and callbacks.
What you need is a queue. Internally, jQuery queues the animations, only executing your callback once its corresponding animation completes. If your callback then starts another animation, the effect is that they are executed in sequence.
In the simplest case this is equivalent to the following:
window.setTimeout(function() {
alert("!");
// set another timeout once the first completes
window.setTimeout(function() {
alert("!!");
}, 1000);
}, 3000); // longer, but first
Here's a general asynchronous looping function. It will call the given functions in order, waiting for the specified number of seconds between each.
function loop() {
var args = arguments;
if (args.length <= 0)
return;
(function chain(i) {
if (i >= args.length || typeof args[i] !== 'function')
return;
window.setTimeout(function() {
args[i]();
chain(i + 1);
}, 2000);
})(0);
}
Usage:
loop(
function() { alert("sam"); },
function() { alert("sue"); });
You could obviously modify this to take configurable wait times or to immediately execute the first function or to stop executing when a function in the chain returns false or to apply the functions in a specified context or whatever else you might need.
I believe the async library will provide you a very elegant way to do this. While promises and callbacks can get a little hard to juggle with, async can give neat patterns to streamline your thought process. To run functions in serial, you would need to put them in an async waterfall. In async lingo, every function is called a task that takes some arguments and a callback; which is the next function in the sequence. The basic structure would look something like:
async.waterfall([
// A list of functions
function(callback){
// Function no. 1 in sequence
callback(null, arg);
},
function(arg, callback){
// Function no. 2 in sequence
callback(null);
}
],
function(err, results){
// Optional final callback will get results for all prior functions
});
I've just tried to briefly explain the structure here. Read through the waterfall guide for more information, it's pretty well written.
your functions should take a callback function, that gets called when it finishes.
function fone(callback){
...do something...
callback.apply(this,[]);
}
function ftwo(callback){
...do something...
callback.apply(this,[]);
}
then usage would be like:
fone(function(){
ftwo(function(){
..ftwo done...
})
});
Since you tagged it with javascript, I would go with a timer control since your function names are 3, 5, and 8 seconds. So start your timer, 3 seconds in, call the first, 5 seconds in call the second, 8 seconds in call the third, then when it's done, stop the timer.
Normally in Javascript what you have is correct for the functions are running one after another, but since it looks like you're trying to do timed animation, a timer would be your best bet.
asec=1000;
setTimeout('some_3secs_function("somevalue")',asec*3);
setTimeout('some_5secs_function("somevalue")',asec*5);
setTimeout('some_8secs_function("somevalue")',asec*8);
I won't go into a deep discussion of setTimeout here, but:
in this case I've added the code to execute as a string. this is the simplest way to pass a var into your setTimeout-ed function, but purists will complain.
you can also pass a function name without quotes, but no variable can be passed.
your code does not wait for setTimeout to trigger.
This one can be hard to get your head around at first: because of the previous point, if you pass a variable from your calling function, that variable will not exist anymore by the time the timeout triggers - the calling function will have executed and it's vars gone.
I have been known to use anonymous functions to get around all this, but there could well be a better way,
You could also use promises in this way:
some_3secs_function(this.some_value).then(function(){
some_5secs_function(this.some_other_value).then(function(){
some_8secs_function(this.some_other_other_value);
});
});
You would have to make some_value global in order to access it from inside the .then
Alternatively, from the outer function you could return the value the inner function would use, like so:
one(some_value).then(function(return_of_one){
two(return_of_one).then(function(return_of_two){
three(return_of_two);
});
});
ES6 Update
Since async/await is widely available now, this is the way to accomplish the same:
async function run(){
await $('#art1').animate({'width':'1000px'},1000,'linear').promise()
await $('#art2').animate({'width':'1000px'},1000,'linear').promise()
await $('#art3').animate({'width':'1000px'},1000,'linear').promise()
}
Which is basically "promisifying" your functions (if they're not already asynchronous), and then awaiting them
//sample01
(function(_){_[0]()})([
function(){$('#art1').animate({'width':'10px'},100,this[1].bind(this))},
function(){$('#art2').animate({'width':'10px'},100,this[2].bind(this))},
function(){$('#art3').animate({'width':'10px'},100)},
])
//sample02
(function(_){_.next=function(){_[++_.i].apply(_,arguments)},_[_.i=0]()})([
function(){$('#art1').animate({'width':'10px'},100,this.next)},
function(){$('#art2').animate({'width':'10px'},100,this.next)},
function(){$('#art3').animate({'width':'10px'},100)},
]);
//sample03
(function(_){_.next=function(){return _[++_.i].bind(_)},_[_.i=0]()})([
function(){$('#art1').animate({'width':'10px'},100,this.next())},
function(){$('#art2').animate({'width':'10px'},100,this.next())},
function(){$('#art3').animate({'width':'10px'},100)},
]);
I use a 'waitUntil' function based on javascript's setTimeout
/*
funcCond : function to call to check whether a condition is true
readyAction : function to call when the condition was true
checkInterval : interval to poll <optional>
timeout : timeout until the setTimeout should stop polling (not 100% accurate. It was accurate enough for my code, but if you need exact milliseconds, please refrain from using Date <optional>
timeoutfunc : function to call on timeout <optional>
*/
function waitUntil(funcCond, readyAction, checkInterval, timeout, timeoutfunc) {
if (checkInterval == null) {
checkInterval = 100; // checkinterval of 100ms by default
}
var start = +new Date(); // use the + to convert it to a number immediatly
if (timeout == null) {
timeout = Number.POSITIVE_INFINITY; // no timeout by default
}
var checkFunc = function() {
var end = +new Date(); // rough timeout estimations by default
if (end-start > timeout) {
if (timeoutfunc){ // if timeout function was defined
timeoutfunc(); // call timeout function
}
} else {
if(funcCond()) { // if condition was met
readyAction(); // perform ready action function
} else {
setTimeout(checkFunc, checkInterval); // else re-iterate
}
}
};
checkFunc(); // start check function initially
};
This would work perfectly if your functions set a certain condition to true, which you would be able to poll. Plus it comes with timeouts, which offers you alternatives in case your function failed to do something (even within time-range. Think about user feedback!)
eg
doSomething();
waitUntil(function() { return doSomething_value===1;}, doSomethingElse);
waitUntil(function() { return doSomethingElse_value===1;}, doSomethingUseful);
Notes
Date causes rough timeout estimates. For greater precision, switch to functions such as console.time(). Do take note that Date offers greater cross-browser and legacy support. If you don't need exact millisecond measurements; don't bother, or, alternatively, wrap it, and offer console.time() when the browser supports it
If method 1 has to be executed after method 2, 3, 4. The following code snippet can be the solution for this using Deferred object in JavaScript.
function method1(){
var dfd = new $.Deferred();
setTimeout(function(){
console.log("Inside Method - 1");
method2(dfd);
}, 5000);
return dfd.promise();
}
function method2(dfd){
setTimeout(function(){
console.log("Inside Method - 2");
method3(dfd);
}, 3000);
}
function method3(dfd){
setTimeout(function(){
console.log("Inside Method - 3");
dfd.resolve();
}, 3000);
}
function method4(){
console.log("Inside Method - 4");
}
var call = method1();
$.when(call).then(function(cb){
method4();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Categories