JavaScript callback running out of order in Chrome? - javascript

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

Related

How to implement a callback in Javascript onload event

I really don't understand... I'm a beginner in Javascript.
I have attached my function onLoadDocument to the document.onload event.
The callback function absolutely have to be executed after function111() and function222() have totally finished their job.
Actually, the callback is executed too soon and it causes a problem to function111 and function222.
How to execute the callback function ONLY when function111 and function222 will have finished their job?
function onLoadDocument(event, callback) {
function111();
function222();
callback();
}
function after() {
firstOpeningWindow = false;
}
document.onload = onLoadDocument(event, after);
The issue is that a callback is a function reference, but this line:
onLoadDocument(event, after)
is a function invocation and therefore runs immediately. Also, it's window that has a load event, not document.
function onLoadDocument(callback) {
function111();
function222();
callback();
}
function after() {
firstOpeningWindow = false;
}
// You have to supply a function reference here. So, to pass arguments
// you'd need to wrap your function invocation in another function that
// will be the callback
window.onload = function() { onLoadDocument(after) };
The problem is that window.onload (as a commenter on another answer said,, document.onload doesn't exist) takes a function, which is executed when the event happens. You're not passing in a function here, you're passing in the return value of onLoadDocument(event, after). This is undefined - and to get that, the browser is executing the function, which is too early for you.
The solution is just to have onLoadDocument return a function:
function onLoadDocument(event, callback) {
return function () {
function111();
function222();
callback();
}
}
function after() {
firstOpeningWindow = false;
}
window.onload = onLoadDocument(event, after);
The function is called when you call the function, so:
document.onload = onLoadDocument(event, after);
… calls onLoadDocument immediately and assigns the return value to onload (which is pointless because the return value is not a function).
If you want to take this approach, then you need to write a factory which generates your onload function using a closure:
function onLoadDocumentFactory(callback) {
function onLoadDocument(event) {
function111();
function222();
callback();
}
return onLoadDocument;
}
function after() {
firstOpeningWindow = false;
}
document.onload = onLoadDocument(after);
That said, it would be easier just to add your functions in order using the modern addEventListener.
function function111() {
console.log(111);
}
function function222() {
console.log(222);
}
function after() {
console.log("after");
}
addEventListener("load", function111);
addEventListener("load", function222);
addEventListener("load", after);

Function with async operation that works for script-tag and as standalone

Let's say I have a function in a file:
foo.js:
(function(){
return {
loadStuff: function(callback){
setTimeout(function() { callback() }, 2000)
}
}
})()
I have to run loadStuff in two different ways:
first: when I get the file loaded by javascript engine itself (let's say via XHR and eval), then I can call it passing a callback (that's already possible)
second: when it's loaded in a script tag, it still should call loadStuff automatically, although without a callback this time
How should I change the code to make loadStuff work both ways? I don't want to call loadStuff twice though: so for the first case I want to call it with callback, not like it would call it the first time function loaded (without callback) and then again with callback. I just need it to run loadStuff once, with callback.
So.
when <script src='foo.js' /> it should call loadStuff automatically
when the function is string and I do this:
fn = eval(fnStr)
fn.loadStuff(function(){
console.log("stuff loaded")
})
It should call loadStuff only once
Try
foo.js:
(function(){
var called = false,
loadStuff = function(callback){
called = true;
setTimeout(function() { callback && callback() }, 2000)
};
setTimeout(function(){!called && loadStuff();}, 13); //autolaunch if needed
return {
loadStuff: loadStuff
}
})()

JavaScript/jQuery callbacks of functions

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?

Call a function after previous function is complete

I have the following JavaScript code:
$('a.button').click(function(){
if (condition == 'true'){
function1(someVariable);
function2(someOtherVariable);
}
else {
doThis(someVariable);
}
});
How can I ensure that function2 is called only after function1 has completed?
Specify an anonymous callback, and make function1 accept it:
$('a.button').click(function(){
if (condition == 'true'){
function1(someVariable, function() {
function2(someOtherVariable);
});
}
else {
doThis(someVariable);
}
});
function function1(param, callback) {
...do stuff
callback();
}
If you're using jQuery 1.5 you can use the new Deferreds pattern:
$('a.button').click(function(){
if(condition == 'true'){
$.when(function1()).then(function2());
}
else {
doThis(someVariable);
}
});
Edit: Updated blog link:
Rebecca Murphy had a great write-up on this here: http://rmurphey.com/blog/2010/12/25/deferreds-coming-to-jquery/
Try this :
function method1(){
// some code
}
function method2(){
// some code
}
$.ajax({
url:method1(),
success:function(){
method2();
}
})
This answer uses promises, a JavaScript feature of the ECMAScript 6 standard. If your target platform does not support promises, polyfill it with PromiseJs.
Promises are a new (and a lot better) way to handle asynchronous operations in JavaScript:
$('a.button').click(function(){
if (condition == 'true'){
function1(someVariable).then(function() {
//this function is executed after function1
function2(someOtherVariable);
});
}
else {
doThis(someVariable);
}
});
function function1(param, callback) {
return new Promise(function (fulfill, reject){
//do stuff
fulfill(result); //if the action succeeded
reject(error); //if the action did not succeed
});
}
This may seem like a significant overhead for this simple example, but for more complex code it is far better than using callbacks. You can easily chain multiple asynchronous calls using multiple then statements:
function1(someVariable).then(function() {
function2(someOtherVariable);
}).then(function() {
function3();
});
You can also wrap jQuery deferrds easily (which are returned from $.ajax calls):
Promise.resolve($.ajax(...params...)).then(function(result) {
//whatever you want to do after the request
});
As #charlietfl noted, the jqXHR object returned by $.ajax() implements the Promise interface. So it is not actually necessary to wrap it in a Promise, it can be used directly:
$.ajax(...params...).then(function(result) {
//whatever you want to do after the request
});
Or you can trigger a custom event when one function completes, then bind it to the document:
function a() {
// first function code here
$(document).trigger('function_a_complete');
}
function b() {
// second function code here
}
$(document).bind('function_a_complete', b);
Using this method, function 'b' can only execute AFTER function 'a', as the trigger only exists when function a is finished executing.
you can do it like this
$.when(funtion1()).then(function(){
funtion2();
})
This depends on what function1 is doing.
If function1 is doing some simple synchrounous javascript, like updating a div value or something, then function2 will fire after function1 has completed.
If function1 is making an asynchronous call, such as an AJAX call, you will need to create a "callback" method (most ajax API's have a callback function parameter). Then call function2 in the callback. eg:
function1()
{
new AjaxCall(ajaxOptions, MyCallback);
}
function MyCallback(result)
{
function2(result);
}
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>
If function1 is some sync function that you want to turn into an async one because it takes some time to complete, and you have no control over it to add a callback :
function function1 (someVariable) {
var date = Date.now ();
while (Date.now () - date < 2000); // function1 takes some time to complete
console.log (someVariable);
}
function function2 (someVariable) {
console.log (someVariable);
}
function onClick () {
window.setTimeout (() => { function1 ("This is function1"); }, 0);
window.setTimeout (() => { function2 ("This is function2"); }, 0);
console.log ("Click handled"); // To show that the function will return before both functions are executed
}
onClick ();
The output will be :
Click handled
...and after 2 seconds :
This is function 1
This is function 2
This works because calling window.setTimeout () will add a task to the JS runtine task loop, which is what an async call makes, and because the basic principle of "run-to-completion" of the JS runtime ensures that onClick () is never interrupted before it ends.
Notice that this as funny as it makes the code difficult to understand...

JavaScript Callback after calling function

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

Categories