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?
Callbacks are not asynchronous by nature, but can be used for asynchronous purposes. In this code, you define a function fn , define a function higherOrderFunction that takes a function callback as an argument, and pass fn as a callback to higherOrderFunction .
There are 2 kinds of callback functions: synchronous and asynchronous. The synchronous callbacks are executed at the same time as the higher-order function that uses the callback. Synchronous callbacks are blocking.
Simply taking a callback doesn't make a function asynchronous. There are many examples of functions that take a function argument but are not asynchronous. For example there's forEach in Array. It iterates over each item and calls the function once per item.
Callbacks. An event handler is a particular type of callback. A callback is just a function that's passed into another function, with the expectation that the callback will be called at the appropriate time. As we just saw, callbacks used to be the main way asynchronous functions were implemented in JavaScript.
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.
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...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With