I am trying to call a function AFTER another function has completed. Normally, this is done with callbacks, at least with Node.js. However, when I try to run the following code in Chrome, the callback function seems to execute before the main function. Am I writing my function/callback wrong? Shouldn't the second function (the callback function) only execute after the first one is complete?
If callbacks don't work when the javascript is running client-side in the browser, is there another way I can ensure the second function runs only when the first function is complete?
<html>
<head></head>
<body>
<script>
function firstLoad(callback) {
console.log("firstLoad function fired.");
}
function secondLoad() {
console.log("secondLoad function fired.");
}
firstLoad(secondLoad());
</script>
</body>
</html>
In Chrome Developer Tools Console, the above code gives me:
secondLoad function fired.
firstLoad function fired.
I would expect it to be the other way around.
I'm trying to give a simpler answer here that gets straight to the point, I have edited your code so it's working the way you are expecting it to work and added some comments to explain what's happening:
<html>
<head></head>
<body>
<script>
function firstLoad(callback) { //secondLoad is "saved" in the callback variable
console.log("firstLoad function fired.");
//When Firstload is done with doing all it has to do you have to manually call
//the callback which references to the secondLoad function:
callback();
}
function secondLoad() {
console.log("secondLoad function fired.");
}
//Here you pass the secondLoad function as a parameter for the firstLoad function,
//in your code you were passing the *result* of secondLoad
firstLoad(secondLoad);
</script>
</body>
</html>
I am assuming that firstLoad is not doing asynchronous stuff like network requests
Expressions in an argument list are evaluated immediately, so that the expression can be passed to the function. So with
firstLoad(secondLoad());
secondLoad is called and evaluated to
firstLoad(undefined);
before firstLoad is called.
If firstLoad is asynchronous, pass just the secondLoad function name instead, and call it as a callback at the end of the asynchronous action:
function firstLoad(callback) {
console.log("firstLoad function fired.");
setTimeout(() => {
console.log('firstload done');
callback();
}, 1000);
}
function secondLoad() {
console.log("secondLoad function fired.");
}
firstLoad(secondLoad);
You can also have firstLoad return a Promise:
function firstLoad() {
console.log("firstLoad function fired.");
return new Promise((resolve) => {
setTimeout(() => {
console.log('firstload done');
resolve();
}, 1000);
});
}
function secondLoad() {
console.log("secondLoad function fired.");
}
firstLoad()
.then(secondLoad);
Of course, if firstLoad isn't asynchronous, just call secondLoad after firstLoad:
function firstLoad(callback) {
console.log("firstLoad function fired.");
}
function secondLoad() {
console.log("secondLoad function fired.");
}
firstLoad();
secondLoad();
Let's say I have multiple functions func1, func2, func3, etc.....
And they all contain an AJAX/async function within them:
function funcX(){
// some ajax request
}
If in a main function I am calling func1, func2, func3 sequentially like so:
$(document).ready(function(){
func1();
func2();
func3();
...
}
Will each ajax/async function's call be certain to execute in the order of their parent functions? At first I thought they might be, but the behavior of my program seems to be suggesting otherwise...
If not, is there a good (hopefully simple?) alternative to having a long chain of callbacks?
Will each ajax/async function's call be certain to execute in the order of their parent functions?
They should execute in order, but their internal callbacks can be called in any order.
If not, is there a good (hopefully simple?) alternative to having a long chain of callbacks?
You could use a promise, and execute the next function when the promise has been resolved.
This example uses jQuery...
var fn1 = function () {
var d = $.Deferred();
setTimeout(function () {
$("body").text("Callback 1 done.") && d.resolve();
}, Math.random() * 1300 + 800);
return d.promise();
};
var fn2 = function () {
var d = $.Deferred();
setTimeout(function () {
$("body").text("Callback 2 done.") && d.resolve();
}, 500);
return d.promise();
};
$.when(fn1(), fn2()).then(function () {
setTimeout(function () {
$("body").text("All done.");
}, 300);
});
jsFiddle.
We use $.when() and pass the invoked functions we want to execute to it. We then use then() to show a final message (I placed a setTimeout() here so you can see the last resolved function's message in the document).
Each of these functions have their own deferred object which return the promise. A setTimeout() mocks an XHR for example's sake. When this callback is executed, we resolve the deferred object.
Once both have been deferred, we reach the callback for then().
To serialize tasks, I've written a helper function, which can also be found in my earlier answer:
function serializeTasks(arr, fn, done)
{
var current = 0;
fn(function iterate() {
if (++current < arr.length) {
fn(iterate, arr[current]);
} else {
done();
}
}, arr[current]);
}
It takes an array of values (in your case those are actually functions), a loop function and a completion handler. Below is the loop function:
function loopFn(nextTask, fn)
{
fn(nextTask);
}
It accepts an intermediate completion function as the first argument and each element of the aforementioned array.
To set everything in motion:
serializeTasks([func1, func2, func3], loopFn, function() {
console.log('all done');
});
Your functions are called with a single argument which should be passed to the AJAX success callback, e.g.
func1(nextTask)
{
$.ajax({
...,
success: nextTask
});
}
The order in which the asynch results are returned is not deterministic, and may wary every time.
func2 might complete before func1 etc
It is important to ensure correct order of execution. One pattern is to call the next function in the success callback of the prior function
Ex:
$.get("/someUrl",function(){
$.get("/nextAjaxCall", function(){
.....
});
});
If the dependency chain is very simple, I don't think it's necessary to introduce a framework to handle this
Or look at async library and it's awesomeness !
async
I have 2 functions.
function f1() {
$.ajax(..., success: function(response) {
// some code executed whenever request is completed.
}
}
function f2() {
// my code
}
I need to call these functions one after another.
f1() // waiting until ajax request in the function is complete.
f2()
I tried $.when().then(), however it didn't seem to work.
The $.ajax call returns an instance of $.Deferred which is used to track the progress of the call - that is what you need to return from your f1 function. You can then use .then(), .done() etc.
Edit in response to comments
If you want to invoke a callback within f1 as well as externally you can return the result of the .pipe method.
function f1() {
return $.ajax(/*...*/).pipe(function() {
//local 'done' handler
});
}
function f2(resultFromF1Handler) {
//...
}
f1().done(f2);
function f1(onsuccess)
{
$.ajax(
{
success: function(r)
{
// some code
onsuccess(r);
}
});
}
function f2()
{
// my code
}
f1(f2);
I suggest calling f2() inside the anonymous function that is executed as f1()'s success.
Is there a problem with doing that?
Check out this code :
Link
<span>Moving</span>
$('#link').click(function () {
console.log("Enter");
$('#link').animate({ width: 200 }, 2000, function() {
console.log("finished");
});
console.log("Exit");
});
As you can see in the console, the "animate" function is asynchronous, and it "fork"s the flow of the event handler block code. In fact :
$('#link').click(function () {
console.log("Enter");
asyncFunct();
console.log("Exit");
});
function asyncFunct() {
console.log("finished");
}
follow the flow of the block code!
If I wish to create my function asyncFunct() { } with this behaviour, how can I do it with javascript/jquery? I think there is a strategy without the use of setTimeout()
You cannot make a truly custom asynchronous function. You'll eventually have to leverage on a technology provided natively, such as:
setInterval
setTimeout
requestAnimationFrame
XMLHttpRequest
WebSocket
Worker
Some HTML5 APIs such as the File API, Web Database API
Technologies that support onload
... many others
In fact, for the animation jQuery uses setInterval.
You can use a timer:
setTimeout( yourFn, 0 );
(where yourFn is a reference to your function)
or, with Lodash:
_.defer( yourFn );
Defers invoking the func until the current call stack has cleared. Any additional arguments are provided to func when it's invoked.
here you have simple solution (other write about it)
http://www.benlesh.com/2012/05/calling-javascript-function.html
And here you have above ready solution:
function async(your_function, callback) {
setTimeout(function() {
your_function();
if (callback) {callback();}
}, 0);
}
TEST 1 (may output '1 x 2 3' or '1 2 x 3' or '1 2 3 x'):
console.log(1);
async(function() {console.log('x')}, null);
console.log(2);
console.log(3);
TEST 2 (will always output 'x 1'):
async(function() {console.log('x');}, function() {console.log(1);});
This function is executed with timeout 0 - it will simulate asynchronous task
Here is a function that takes in another function and outputs a version that runs async.
var async = function (func) {
return function () {
var args = arguments;
setTimeout(function () {
func.apply(this, args);
}, 0);
};
};
It is used as a simple way to make an async function:
var anyncFunction = async(function (callback) {
doSomething();
callback();
});
This is different from #fider's answer because the function itself has its own structure (no callback added on, it's already in the function) and also because it creates a new function that can be used.
Edit: I totally misunderstood the question. In the browser, I would use setTimeout. If it was important that it ran in another thread, I would use Web Workers.
Late, but to show an easy solution using promises after their introduction in ES6, it handles asynchronous calls a lot easier:
You set the asynchronous code in a new promise:
var asyncFunct = new Promise(function(resolve, reject) {
$('#link').animate({ width: 200 }, 2000, function() {
console.log("finished");
resolve();
});
});
Note to set resolve() when async call finishes.
Then you add the code that you want to run after async call finishes inside .then() of the promise:
asyncFunct.then((result) => {
console.log("Exit");
});
Here is a snippet of it:
$('#link').click(function () {
console.log("Enter");
var asyncFunct = new Promise(function(resolve, reject) {
$('#link').animate({ width: 200 }, 2000, function() {
console.log("finished");
resolve();
});
});
asyncFunct.then((result) => {
console.log("Exit");
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Link
<span>Moving</span>
or JSFiddle
This page walks you through the basics of creating an async javascript function.
Since ES2017, asynchronous javacript functions are much easier to write. You should also read more on Promises.
If you want to use Parameters and regulate the maximum number of async functions you can use a simple async worker I've build:
var BackgroundWorker = function(maxTasks) {
this.maxTasks = maxTasks || 100;
this.runningTasks = 0;
this.taskQueue = [];
};
/* runs an async task */
BackgroundWorker.prototype.runTask = function(task, delay, params) {
var self = this;
if(self.runningTasks >= self.maxTasks) {
self.taskQueue.push({ task: task, delay: delay, params: params});
} else {
self.runningTasks += 1;
var runnable = function(params) {
try {
task(params);
} catch(err) {
console.log(err);
}
self.taskCompleted();
}
// this approach uses current standards:
setTimeout(runnable, delay, params);
}
}
BackgroundWorker.prototype.taskCompleted = function() {
this.runningTasks -= 1;
// are any tasks waiting in queue?
if(this.taskQueue.length > 0) {
// it seems so! let's run it x)
var taskInfo = this.taskQueue.splice(0, 1)[0];
this.runTask(taskInfo.task, taskInfo.delay, taskInfo.params);
}
}
You can use it like this:
var myFunction = function() {
...
}
var myFunctionB = function() {
...
}
var myParams = { name: "John" };
var bgworker = new BackgroundWorker();
bgworker.runTask(myFunction, 0, myParams);
bgworker.runTask(myFunctionB, 0, null);
Function.prototype.applyAsync = function(params, cb){
var function_context = this;
setTimeout(function(){
var val = function_context.apply(undefined, params);
if(cb) cb(val);
}, 0);
}
// usage
var double = function(n){return 2*n;};
var display = function(){console.log(arguments); return undefined;};
double.applyAsync([3], display);
Although not fundamentally different than the other solutions, I think my solution does a few additional nice things:
it allows for parameters to the functions
it passes the output of the function to the callback
it is added to Function.prototype allowing a nicer way to call it
Also, the similarity to the built-in function Function.prototype.apply seems appropriate to me.
Next to the great answer by #pimvdb, and just in case you where wondering, async.js does not offer truly asynchronous functions either. Here is a (very) stripped down version of the library's main method:
function asyncify(func) { // signature: func(array)
return function (array, callback) {
var result;
try {
result = func.apply(this, array);
} catch (e) {
return callback(e);
}
/* code ommited in case func returns a promise */
callback(null, result);
};
}
So the function protects from errors and gracefully hands it to the callback to handle, but the code is as synchronous as any other JS function.
Unfortunately, JavaScript doesn't provide an async functionality. It works only in a single one thread. But the most of the modern browsers provide Workers, that are second scripts which gets executed in background and can return a result.
So, I reached a solution I think it's useful to asynchronously run a function, which creates a worker for each async call.
The code below contains the function async to call in background.
Function.prototype.async = function(callback) {
let blob = new Blob([ "self.addEventListener('message', function(e) { self.postMessage({ result: (" + this + ").apply(null, e.data) }); }, false);" ], { type: "text/javascript" });
let worker = new Worker(window.URL.createObjectURL(blob));
worker.addEventListener("message", function(e) {
this(e.data.result);
}.bind(callback), false);
return function() {
this.postMessage(Array.from(arguments));
}.bind(worker);
};
This is an example for usage:
(function(x) {
for (let i = 0; i < 999999999; i++) {}
return x * 2;
}).async(function(result) {
alert(result);
})(10);
This executes a function which iterate a for with a huge number to take time as demonstration of asynchronicity, and then gets the double of the passed number.
The async method provides a function which calls the wanted function in background, and in that which is provided as parameter of async callbacks the return in its unique parameter.
So in the callback function I alert the result.
MDN has a good example on the use of setTimeout preserving "this".
Like the following:
function doSomething() {
// use 'this' to handle the selected element here
}
$(".someSelector").each(function() {
setTimeout(doSomething.bind(this), 0);
});
Ok so lets say I have this function:
function a(message) {
alert(message);
}
And I want to have a callback after the alert window is shown. Something like this:
a("Hi.", function() {});
I'm not sure how to have a callback inside of the function I call like that.
(I'm just using the alert window as an example)
Thanks!
There's no special syntax for callbacks, just pass the callback function and call it inside your function.
function a(message, cb) {
console.log(message); // log to the console of recent Browsers
cb();
}
a("Hi.", function() {
console.log("After hi...");
});
Output:
Hi.
After hi...
You can add a if statement to check whether you add a callback function or not. So you can use the function also without a callback.
function a(message, cb) {
alert(message);
if (typeof cb === "function") {
cb();
}
}
Here is the code that will alert first and then second. I hope this is what you asked.
function basic(callback) {
alert("first...");
var a = "second...";
callback(a);
}
basic(function (abc) {
alert(abc);
});