I have a question about asynchronicity in Javascript. From what I've been able to read up on about is that Javascript only uses one thread but is able to handle events asynchronously.
I have the following code:
game.next();
this.autopilot();
Now, I need the function game.next to finish before the this.autopilot function is called. Does Javascript actually wait until game.next is finished or does it run this.autopilot istantly?
If it does, does a callback solve the problem?
The next function with a callback:
Game.prototype.next = function(callback) {
// Code
if(callback !== undefined) {
callback();
}
};
The calling function using that callback:
game.next(function() {
this.autopilot();
}.bind(this));
In your code, game.next() must return before this.autopilot() is called.
However, it is possible that game.next() starts an asynchronous process (e.g., an Ajax call) and returns before the process completes. If you want to defer execution of this.autopilot() until that process is complete, then you would need some sort of callback mechanism. game.next() would have to pass the callback function on to whatever asynchronous processing was taking place. (Executing the call-back just before returning—as you suggest in your question—would be wrong, as this would also happen before the asynchronous process completed.)
Yes, you can use a callback to make sure game.next() finishes before this.autopilot(). But there is a trick: you just cannot paste some callback after whatever code. Whether or not you need to implement a callback depends on whether game.next() function is asynchronous.
Example of a synchronous function that doesn't need a callback:
function next() {
Game.x += 1;
}
Example of an asynchronous function that needs a callback to preserve the execution order:
function next(callback) {
window.setTimeout(function() {
if(callback !== undefined) { /* etc. */ }
}, 0);
}
Related
I am reading various tutorials on javascript with callbacks, but they are not clarifying a basic principle. What I understand is that when a function is called with a callback, this makes it possible to wait on execution of some code, and after that execute the callback function. So
// function definition
do_func(int num, callback)
{
Console.log(num);
..
..
callback();
}
//call the function
do_func(123, function(){
Console.log("Running callback");
});
So during execution of do_func, all lines of code are executed, and then callback() is executed. However if our function changed as
// function definition
do_func(int num, callback)
{
read file...
..
..
callback();
}
Then the callback would be called while the file is being read. So our purpose is defeated. What is happening here?
Thanks very much!
JavaScript itself is synchronous and single-threaded. You cannot write an asynchronous function.
What you can do is use some APIs provided by your environment (Node.js, Web browser) that allow you to schedule asynchronous tasks... timeouts, ajax, FileAPI to name a few.
An example using setTimeout (provided by the HTML Timing API):
window.setTimeout(function() {
console.log("World");
}, 1000);
console.log("Hello");
What is a simple example of an asynchronous javascript function?
do_func(int num, callback)
{
readFile(... , callback);
}
I'd expect that you're readFile function can also take a callback so you could just pass it along until the file has been read.
You are correct that in your example callback() would be called right away without waiting for the file to load. In JavaScript anytime something asynchronous is encountered you can generally listen for an event to know when to run the callback function.
Example loading an image:
var img = new Image();
img.addEventListener('load', callback); //add your listener first because if you ran the next line first it might load before your listener is setup
img.src = "fileondisk.jpg";
I am using the Github API to retrieve data about one of my repos and I am running into trouble with callback functions and recursive functions overlapping (as in recursive functions with callbacks attached to them)
Here is the script on jsfiddle as well as below:
(function () {
'use strict';
function makeAJAXCall(hash, cb) {
$.ajaxSetup({
accept: 'application/vnd.github.raw',
dataType: 'jsonp'
});
$.ajax({
url: hash,
success: function (json) {
//console.info(json);
// Time for callback to be executed
if (cb) {
cb(json);
}
},
error: function (error) {
console.error(error);
// an error happened, check it out.
throw error;
}
});
}
function parseBlob(hash) {
return makeAJAXCall(hash, function (returnedJSON) { // no loop as only one entry
console.log(returnedJSON.data);
return returnedJSON.data.content;
});
}
function walkTree(hash) {
var tree = 'https://api.github.com/repos/myusername/SVG-Shapes/git/trees/' + hash;
return makeAJAXCall(tree, function (objectedJSON) {
var objectList = [], i, entry;
for (i = 0; i < objectedJSON.data.tree.length; i += 1) {
entry = objectedJSON.data.tree[i];
//console.debug(entry);
if (entry.type === 'blob') {
if (entry.path.slice(-4) === '.svg') { // we only want the svg images not the ignore file and README etc
//console.info(entry.path)
objectList.push(parseBlob(entry.url));
}
} else if (entry.type === 'tree') {
objectList.push(walkTree(entry.sha));
}
}
if (cb) {
console.log(objectList);
cb(objectList);
}
return objectList;
});
}
$(document).ready(function () {
var returnedObjects = walkTree('master', function (objects) { // master to start at the top and work our way down
console.info(objects);
});
});
}());
The JSON returned is either blog (file) or tree (directory). If it is a tree the walkTree function is called again. I do not understand how the callback will behave here as well as how to get the data it (should) return(s) out of the function and into the final block at the very bottom.
Can someone clarify how I should be doing this?
Ajax calls are usually asynchronous. That means that when you make the ajax call, it just initiates the ajax call and it finishes some time later. Meanwhile, the rest of your code after the initiation of the ajax call keeps running until completion.
Then, sometime later when the ajax call finishes, the success function is called and, in your case, the callback function gets called by the success function. It is important to understand that the success function is called much later after the makeAJAXCall() function has already finished.
Thus, you cannot return the ajax data from the makeAJAXCall() function because it isn't known yet when that function returns.
In fact, the only two places you can use the results of the ajax call are:
In the success handler directly
In some function that the success handler calls which in your case it the callback function.
So, it is doing you no good to return returnedJSON.data.content; from the callback function. That is just returning into some internal part of the ajax infrastructure and doing nothing. That return value will just be dropped on the floor and lost.
Instead, you need to put whatever code wants to use returnedJSON.data.content right there in that callback function (or pass it to another function with a function call).
Ajax is asynchronous. That means you can't do normal sequential programming when using ajax. Instead, you have to do event driven programming where the event in this case is the callback that is called upon a success completion of the ajax call. All work that uses those ajax results needs to commence from that success handler or the callback that is called from it.
I'm trying to send a request and then in callback function, change the parameters and then send the request again. Something like this:
function sendRequest() {
params = {param1:'value', param2:'value'};
while(params) {
$.getJSON("url", params, function(data) {
if(data contains something important)
params.foo = bar;
else
params = null;
});
}
}
But params never changes and the while loop continues for ever. It seems to be a reference problem; but I can't figure out how to solve this. Thanks in advance.
The problem is that getJSON is asynchronous.
while(params) executes. It's truthy, so we continue
$.getJSON is called. The function passed to it will not be called at this time. It's just queued to the subsystem that later will perform the actual request - asynchronously.
while(params) executes again. The callback passed to getJSON has not gotten a chance to run yet, so params is unchanged.
Go to step 2.
In other words, you created an infinite loop, since the system that processes the queued callback function never gets to execute because of the infinite loop. All you end up doing is creating a infinitely long list of queued getJSON calls.
A better solution would be something like this:
function sendRequest(params) {
$.getJSON("url", params, function(data) {
if(data contains something important)
params.foo = bar;
sendRequest(params);
else
params = null;
// Probably do something like requestChainFinished(params);
});
}
sendRequest({param1:'value', param2:'value'});
By using a function, you take control of the flow and don't perform another request until the asynchronous callback to getJSON has been called.
'params' is a closure variable. The value of params changes at a later time in the callback when the asynchronous ajax call's response arrives, but your while loop is keeping the JS engine busy that the callback isn't going to get called ever.
Either you could keep calling the same function in the response callback or you can use async: false like this $.ajax({ async: false, ...
Let's say for example that I have two functions with random code inside and also that based on the user's system (slow, medium, or fast) there is no way to tell how long the two functions will take to complete, so the use of setTimeout is not practical when trying to fire function2 only after function1 is complete.
How can you use jQuery.deferred to make function2 fire only after function1 no matter what the time requirements are, and considering that both functions are 100% non-jQuery functions with no jQuery code inside them and therefore completely un-observable by jQuery? At the very most, the functions might include jQuery methods like .css() which do not have a time association and can run slower on old computers.
How do I assure that function2 is not executing at the same time as function1 if I call them like this:
function1(); function2();
using $.deferred? Any other answers besides those regarding $.deferred are also welcome!
ADDED March 20:
What if function1() is a lambda function where, depending on user input, the function may or may not have asynchronous calls and it is not possible to tell how many operations the function will do? It'd be a function where you wouldn't have any clue as to what would happen next, but no matter what, you'd still want function2 to execute only after everything from the lambda function (function1) is done, no matter how long it takes but as long as the asynchronous aspects are completed. How can this be achieved?
ADDED March 22:
So I guess the only way to do what I'm asking is to pass anonymous functions as callbacks to asynchromous functions that execute the callbacks after they are done, or to create event listeners that will do execute what you want when the event is finally triggered.
There's not really any way to just execute to asynchronous calls on two seperate lines and have them fire in order without manually constructing mechanisms (event handlers) within the frame containing the said functions to handle the actual execution of their actions.
A good example of these types of mechanisms would be jQuery's .queue() method and $.Defferred object.
The answers below along with reading up on jQuery's API on .queue()ing and using $.Deferred helped clarify this.
Tgr gave a great example below on how to create custom chainable functions using jQuery's $.Deferred object, and the custom functions themselves don't necessarily have to have any jQuery code inside them, which is exactly what I was looking for.
function first(deferred) {
// do stuff
deferred.resolve();
}
function second() {
// do stuff
}
$.Deferred(first).then(second);
But as Tomalak pointed out, this is unnecessary, unless you do something very tricky in first (like utilising web workers).
Update:
The basic idea is that whenever you do something that is not immediate, you create a Deferred object, and return that. (jQuery's AJAX calls already do this.) You can then use Deferred.then to delay follow-up operations.
function first() {
var deferred = $.Deferred();
var callback = function() {
deferred.resolve();
}
// do immediate stuff
someAsyncOperation(callback);
return deferred.promise(); // turns the Deferred into a Promise, which
// means that resolve() will not be accessible
}
function second() {
// do stuff
}
first().then(second); // or: $.when(first).then(second)
If second is also an asynchronous operation, you can use $.when's merging capabilities:
function second() {
var anotherDeferred = $.Deferred();
// do stuff with anotherDeferred
return anotherDeferred.promise();
}
$.when(first(), second()).then(third); // third will run at the moment when
// both first and second are done
JavaScript itself is not asynchronous. It is single-threaded, synchronous.
function1();
function2();
will execute one after another unless they contain asynchronous calls. In that case, there will always be a callback you can pass (like onSuccess for XmlHttpRequest). Place the second function there.
To say the truth, they strictly execute one after another even if they contain asynchronous bits. It's just that the asynchronous bits might not yet be finished when the rest of the function is.
EDIT Your jsFiddle example, fixed (see it):
function foo() {
$('#foo')
.html('<span>foo1</span>')
.animate(
{ /* properties */
left: '100px'
},
360, /* duration */
'swing', /* easing */
function () { /* "complete" callback */
$('#foo').append('<span>foo2</span>');
bar();
}
);
}
As I said. There will always be a callback you can pass.
I need to write a function in JavaScript, which returns a state from calling an asynchronous function. However, the caller only receives the value and no callback function is to be provided. I tried something like:
function getState() {
var ret = null;
asyncCall("request",
function() { ret = "foo"; } // callback
);
while (ret === null)
; // block on the asynchronous call
return ret;
}
However, the loop is never going to end…
Any ideas? Thank you.
I think you're looking for StratifiedJS, http://stratifiedjs.org
It allows you to "orchestrate" your async code exactly like you wrote, BEWARE that it "writes" like synchronous code, it will NOT block the rest of your application.
You can use it anywhere by loading the apollo js library.
This is how it would look like in Stratified JavaScript:
function getState() {
waitfor (var ret) {
// block on the asynchronous call
asyncCall("request", resume);
}
return ret;
}
of course there are modules/libraries that would just make it look like this:
http://onilabs.com/modules#http
function getState() {
return http.get("request");
}
Why not just:
asyncCall("request", function() {
// here you can inspect the state
});
What's the point of the wrapper function?
Asynchronous functions work this way. If you want to block the execution, then use a synchronous call:
var state = syncCall("request");
The best would be to put the logic you want to have called into the callback function. Is there any reason why you can't do that?
You could use setInterval to check on the result, but that requires a callback function, too...
Unless I misunderstood your question, you could look at jQuery's ajaxStop() event - http://api.jquery.com/ajaxstop. This blocks until all ajax calls have completed. This obviously requires that all of your async calls be done thru jQuery.
$(document).ajaxStop(function() {
// do your "ajax dependent" stuff here
});
what you are trying to do is a synchronous call, so its not a good idea to do it with a function designed for asynchronous call.
You have an answer here : How can I get jQuery to perform a synchronous, rather than asynchronous, Ajax request?