9

This example is JavaScript, since that's where I'm using callbacks mostly. I want to understand how they work at a low level.

In the example below, I'd expect everything to happen in order and "calling back" to occur after "step 3" and before "step 4." This makes sense to me, as everything is done in order on a single thread of execution. There's no trickery. The only thing that is sort of special is that you've passed a function to another function.

function main() {
  console.log("step 1");
  console.log("step 2");
  doSomething(myCallBack);
  console.log("step 4");
}

function doSomething(f) {
  accessTheDatabase(); // this could take a while
  console.log("step 3");
  f(); // done - now call back
}

function myCallBack() {
  console.log("calling back!");
}

How would you make doSomething asynchronous so that "step 4" can be executed before, or in parallel with, "step 3"?

I assume that if doSomething were somehow called asynchronously, it would have to be on a diffferent thread, no? And if so, when it finishes and then calls myCallBack, does the callback happen on the second thread or on the main thread? If it happens on the main thread, why does the second thread even need a pointer to the callback function? How does the inter-thread call work?

Rob Sobers
  • 20,737
  • 24
  • 82
  • 111
  • I assume this is *not* running in a browser, right? Because in the browser, any AJAX call is necessarily asynchronous... – Dean Harding Oct 15 '10 at 03:36
  • 2
    @Dean, first of all XHR requests can by synchronous (depending on request parameters). Second of all, if you read the question (and look at its example code), it is not about Ajax. – eyelidlessness Oct 15 '10 at 03:38
  • @Dean it could be running in a browser or not. I'm most interested in figuring out whether the callback function is executed on the main thread or the second thread that was passed the callback function pointer as a parameter. – Rob Sobers Oct 15 '10 at 04:12

4 Answers4

11

WebWorkers aside, the JavaScript programming model in the browser is purely single-threaded. You can make your call somewhat asynchronous by using window.setTimeout:

window.setTimeout(doSomething, 0, myCallBack);

This effectively places the call doSomething(myCallBack) onto the timer queue, and after 0 or more milliseconds elapse, it will eventually get invoked. However, as with all asynchronous calls in JavaScript, you must relinquish the execution context before any asynchronous callbacks can be invoked; that is, the timer queue will not be processed (and therefore doSomething(myCallBack) will not be invoked) until your main() function finishes, assuming that is the end of your JavaScript.

One unfortunate consequence of this setTimeout-based approach is that doSomething(myCallBack) doesn't get invoked in parallel alongside console.log("step 4"). On the other hand, consider XMLHttpRequest.send; after making this call, the rest of your JS can continue to execute while the browser issues the HTTP request. Your script does need to finish executing before the onreadystatechange handler can execute, but most of the HTTP connection work can happen in parallel while JS executes.

ide
  • 19,942
  • 5
  • 64
  • 106
  • In the `XMLHttpRequest.send` case, I assume that happens on another thread. When it's done and the `onreadystatechange` handler executes, which thread does that happen on? – Rob Sobers Oct 15 '10 at 04:07
  • 1
    This happens on the main JavaScript thread. Implementation-wise, this can be any thread, but conceptually JavaScript (and the DOM, for that matter) executes within a single thread. – ide Oct 15 '10 at 05:45
  • 1
    I guess the confusing part is that it's called a "callback." That, to me, says "here's a function, please call this when you're done doing your thing. I'll be off doing my own thing." When really, the browser finishes the AJAX call and then interrupts the main thread (via what mechanism??) and the main thread itself calls the function that it registered as the callback. Am I right? – Rob Sobers Oct 16 '10 at 03:12
  • 3
    Conceptually, there are no interrupts. For a moment, pretend that the world has no concurrency at all—you ask the browser to make an AJAX call or to react to mouse clicks, but it cannot do anything until your JS finishes. Only after this point can the browser decide to notify you that the server responded to your AJAX call or the user clicked the page; the JS interpreter starts up once more and runs your callback. (This isn't too far-fetched—the browser cannot process click events, etc. while your JS runs.) – ide Oct 17 '10 at 08:37
5

Hmmm.. something looks wrong (I have done v little JavaScript): you pass myCallBack to the function doSomething() but you dont call it back!? You would have to have a call to f() inside doSomething() or pass it to another function which will call it back once your long operation is complete.. And no callbacks are not inherently asynchronous - in your case you are running it all on the same thread (even if accessTheDatabase() is asynchronous in which case it would immediately return!) - so it will still go Step1, Step2, Step3, Step4. I beleive you want:

function main() {
  console.log("step 1");
  console.log("step 2");
  doSomething(myCallBack);
  console.log("step 4");
}

function doSomething(f) {
  accessTheDatabase(f); // assuming this is an asynchronous operation and calls the callback f once done
}

function myCallBack() {
   console.log("step 3");
}

In answer to the second part of your question: the callback would be run on which ever thread you are calling it from - to run a callback on another thread you would have to join() that thread first then invoke() or begininvoke() the callback - though there may already be some built in way to dispatch your callback onto a queue of things to be run on the thread you want to run it on (often the case with UI threads) NOTE: It may already be that accessTheDatabase() does run the callback on the thread that called it - using one of the afore mentioned methods...

markmnl
  • 11,116
  • 8
  • 73
  • 109
0

You would need multiple threads of execution to do this, which I do not believe you can do in Javascript (although correct me if I'm wrong). However if you were writing a C/C++ program, look at the pthreads package (or libdispatch on Mac OS X 10.6).

Edit: a Google search for "threads javascript" turns up some possibly interesting results.

0

Using callbacks the way you are (function declaration name or function expression variable name as an argument to another function), they are synchronous. To make them asynchronous, you can wrap them in a setTimeout.

eyelidlessness
  • 62,413
  • 11
  • 90
  • 94