开发者

How does Asynchronous Javascript Execution happen? and when not to use return statement?

开发者 https://www.devze.com 2023-03-29 12:28 出处:网络
// synchronous Javascript var result = db.get(\'select * from table1\'); console.log(\'I am syncronous\');
// synchronous Javascript
var result = db.get('select * from table1');
console.log('I am syncronous');

// 开发者_StackOverflow中文版asynchronous Javascript 
db.get('select * from table1', function(result){
    // do something with the result
});
console.log('I am asynchronous')

I know in synchronous code, console.log() executes after result is fetched from db, whereas in asynchronous code console.log() executes before the db.get() fetches the result.

Now my question is, how does the execution happen behind the scenes for asynchronous code and why is it non-blocking?

I have searched the Ecmascript 5 standard to understand how asynchronous code works but could not find the word asynchronous in the entire standard.

And from nodebeginner.org I also found out that we should not use a return statement as it blocks the event loop. But nodejs api and third party modules contain return statements everywhere. So when should a return statement be used and when shouldn't it?

Can somebody throw some light on this?


First of all, passing a function as a parameter is telling the function that you're calling that you would like it to call this function some time in the future. When exactly in the future it will get called depends upon the nature of what the function is doing.

If the function is doing some networking and the function is configured to be non-blocking or asychronous, then the function will execute, the networking operation will be started and the function you called will return right away and the rest of your inline javascript code after that function will execute. If you return a value from that function, it will return right away, long before the function you passed as a parameter has been called (the networking operation has not yet completed).

Meanwhile, the networking operation is going in the background. It's sending the request, listening for the response, then gathering the response. When the networking request has completed and the response has been collected, THEN and only then does the original function you called call the function you passed as a parameter. This may be only a few milliseconds later or it may be as long as minutes later - depending upon how long the networking operation took to complete.

What's important to understand is that in your example, the db.get() function call has long since completed and the code sequentially after it has also executed. What has not completed is the internal anonymous function that you passed as a parameter to that function. That's being held in a javascript function closure until later when the networking function finishes.

It's my opinion that one thing that confuses a lot of people is that the anonymous function is declared inside of your call to db.get and appears to be part of that and appears that when db.get() is done, this would be done too, but that is not the case. Perhaps that would look less like that if it was represented this way:

function getCompletionfunction(result) {
    // do something with the result of db.get
}

// asynchronous Javascript 
db.get('select * from table1', getCompletionFunction);

Then, maybe it would be more obvious that the db.get will return immediately and the getCompletionFunction will get called some time in the future. I'm not suggesting you write it this way, but just showing this form as a means of illustrating what is really happening.

Here's a sequence worth understanding:

console.log("a");
db.get('select * from table1', function(result){
    console.log("b");
});
console.log("c");

What you would see in the debugger console is this:

a
c
b

"a" happens first. Then, db.get() starts its operation and then immediately returns. Thus, "c" happens next. Then, when the db.get() operation actually completes some time in the future, "b" happens.


For some reading on how async handling works in a browser, see How does JavaScript handle AJAX responses in the background?


jfriend00's answer explains asynchrony as it applies to most users quite well, but in your comment you seemed to want more details on the implementation:

[…] Can any body write some pseudo code, explaining the implementation part of the Ecmascript specification to achieve this kind of functionality? for better understanding the JS internals.

As you probably know, a function can stow away its argument into a global variable. Let's say we have a list of numbers and a function to add a number:

var numbers = [];
function addNumber(number) {
    numbers.push(number);
}

If I add a few numbers, as long as I'm referring to the same numbers variable as before, I can access the numbers I added previously.

JavaScript implementations likely do something similar, except rather than stowing numbers away, they stow functions (specifically, callback functions) away.

The Event Loop

At the core of many applications is what's known as an event loop. It essentially looks like this:

  • loop forever:
    • get events, blocking if none exist
    • process events

Let's say you want to execute a database query like in your question:

db.get("select * from table", /* ... */);

In order to perform that database query, it will likely need to perform a network operation. Since network operations can take a significant amount of time, during which the processor is waiting, it makes sense to think that maybe we should, rather than waiting rather than doing some other work, just have it tell us when it's done so we can do other things in the mean time.

For simplicity's sake, I'll pretend that sending will never block/stall synchronously.

The functionality of get might look like this:

  • generate unique identifier for request
  • send off request (again, for simplicity, assuming this doesn't block)
  • stow away (identifier, callback) pair in a global dictionary/hash table variable

That's all get would do; it doesn't do any of the receiving bit, and it itself isn't responsible for calling your callback. That happens in the process events bit. The process events bit might look (partially) like this:

  • is the event a database response? if so:
    • parse the database response
    • look up the identifier in the response in the hash table to retrieve the callback
    • call the callback with the received response

Real Life

In real life, it's a little more complex, but the overall concept is not too different. If you want to send data, for example, you might have to wait until there's enough space in the operating system's outgoing network buffers before you can add your bit of data. When reading data, you might get it in multiple chunks. The process events bit probably isn't one big function, but itself just calling a bunch of callbacks (which then dispatch to more callbacks, and so on…)

While the implementation details between real life and our example are slightly different, the concept is the same: you kick off ‘doing something’, and a callback will be called through some mechanism or another when the work is done.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号