Console.Log() Async or Sync

console.log() async or sync?

console.log is not standardized, so the behavior is rather undefined, and can be changed easily from release to release of the developer tools. Your book is likely to be outdated, as might my answer soon.

To our code, it does not make any difference whether console.log is async or not, it does not provide any kind of callback or so; and the values you pass are always referenced and computed at the time you call the function.

We don't really know what happens then (OK, we could, since Firebug, Chrome Devtools and Opera Dragonfly are all open source). The console will need to store the logged values somewhere, and it will display them on the screen. The rendering will happen asynchronously for sure (being throttled to rate-limit updates), as will future interactions with the logged objects in the console (like expanding object properties).

So the console might either clone (serialize) the mutable objects that you did log, or it will store references to them. The first one doesn't work well with deep/large objects. Also, at least the initial rendering in the console will probably show the "current" state of the object, i.e. the one when it got logged - in your example you see Object {}.

However, when you expand the object to inspect its properties further, it is likely that the console will have only stored a reference to your object and its properties, and displaying them now will then show their current (already mutated) state. If you click on the +, you should be able to see the bar property in your example.

Here's a screenshot that was posted in the bug report to explain their "fix":

Sample Image

So, some values might be referenced long after they have been logged, and the evaluation of these is rather lazy ("when needed"). The most famous example of this discrepancy is handled in the question Is Chrome's JavaScript console lazy about evaluating arrays?

A workaround is to make sure to log serialized snapshots of your objects always, e.g. by doing console.log(JSON.stringify(obj)). This will work for non-circular and rather small objects only, though. See also How can I change the default behavior of console.log in Safari?.

The better solution is to use breakpoints for debugging, where the execution completely stops and you can inspect the current values at each point. Use logging only with serialisable and immutable data.

is node.js' console.log asynchronous?

Update: Starting with Node 0.6 this post is obsolete, since stdout is synchronous now.

Well let's see what console.log actually does.

First of all it's part of the console module:

exports.log = function() {
process.stdout.write(format.apply(this, arguments) + '\n');
};

So it simply does some formatting and writes to process.stdout, nothing asynchronous so far.

process.stdout is a getter defined on startup which is lazily initialized, I've added some comments to explain things:

.... code here...
process.__defineGetter__('stdout', function() {
if (stdout) return stdout; // only initialize it once

/// many requires here ...

if (binding.isatty(fd)) { // a terminal? great!
stdout = new tty.WriteStream(fd);
} else if (binding.isStdoutBlocking()) { // a file?
stdout = new fs.WriteStream(null, {fd: fd});
} else {
stdout = new net.Stream(fd); // a stream?
// For example: node foo.js > out.txt
stdout.readable = false;
}

return stdout;
});

In case of a TTY and UNIX we end up here, this thing inherits from socket. So all that node bascially does is to push the data on to the socket, then the terminal takes care of the rest.

Let's test it!

var data = '111111111111111111111111111111111111111111111111111';
for(var i = 0, l = 12; i < l; i++) {
data += data; // warning! gets very large, very quick
}

var start = Date.now();
console.log(data);
console.log('wrote %d bytes in %dms', data.length, Date.now() - start);

Result

....a lot of ones....1111111111111111
wrote 208896 bytes in 17ms

real 0m0.969s
user 0m0.068s
sys 0m0.012s

The terminal needs around 1 seconds to print out the sockets content, but node only needs 17 milliseconds to push the data to the terminal.

The same goes for the stream case, and also the file case gets handle asynchronous.

So yes Node.js holds true to its non-blocking promises.

How Does All Console Logs Get Executed First In Node.js?

It is fact, the functions in NodeJs and JavaScript is synchronous, however, you can change your function to asynchronous. Below I quoted the example.

1. resetPassword: async function (req, res, next) {
2. let user = await userService.resetPassword(res.body.email);
3. console.log(user);
4. }

In this above example, I created the function based on asynchronous and in line 2 will wait to process until response.

Finally, line 3 will print some information about user.

async vs sync flows and how to ensure ordering

TL;DR await only does something useful with promises.¹ So if you use it with a non-promise (like a function using XMLHttpRequst or setTimeout), await doesn't do anything useful.

In the first case, I would expect a potential case where retVal is undefined when the console.log call is made due to the sync nature of the flow.

Only if foo returns undefined. retVal will always be set to the return value from foo, synchronously. It won't be left unchanged and then assigned to later. (See below for what I think you were really asking.)

In the second case, I would expect that retVal is guaranteed to have an answer by the time it hits the console.log, due to the await.

See above, retVal will always receive the value from foo even without await.

This led me to a 3rd code attempt:

const retVal = await foo()
retVal.then(console.log(retVal))

That's incorrect. You can be certain that retVal will not contain a promise (or other thenable) when you use await, because by definition await waits for promises to settle.

I suspect your real code was more like this:

let retVal;
foo(data => {
retVal = data;
});
console.log(retVal);

In that situation retVal won't be assigned to until or unless foo calls the callback you've given it, If foo does something asynchronous and doesn't call the callback until the asynchronous process is complete, retVal will not have been written to as of the console.log. And it doesn't matter whether you use await or not, assuming foo doesn't return a promise. (And if it does, it's unusual for it to also accept a callback, though there are some hybrid APIs out there.)

If foo is something that uses a callback style, and you want to use await with it, you have to wrap it in a promise as described in the answers to this question. There's no reason to do that with XMLHttpRequest because we have fetch, but it's useful for other API functions.

Then, when you have something that returns a promise, using await makes sense:

let retVal = await somethingThatReturnsAPromise();

Here's some questions with lots of useful answers in relation to this stuff:

  • In JavaScript how do I/should I use async/await with XMLHttpRequest?
  • How do I convert an existing callback API to promises? (also linked above)
  • How do I return the response from an asynchronous call?
  • Why is my variable unaltered after I modify it inside of a function?

¹ Or more generally, thenables.

Synchronous console logging in Chrome

You could create a copy of the object before passing it to console.log. Look here for a function to create a deep copy of your object.

Edit:

Now implemented in Chrome, see here

Are javascript's async functions actually synchronous?

Just like when constructing a Promise, anything synchronous inside an async function before any awaits are encountered will execute synchronously. An async function will only stop executing its code once it encounters an await - until then, it may as well be a normal non-async function (except for the fact that it'll wrap the return value in a Promise).

async function asyncFunc2() {

console.log("in Async function 2");

}

async function asyncFunc1() {

console.log("in Async function 1");

await asyncFunc2();

console.log('After an await');

}

console.log("starting sync code");

asyncFunc1().then(() => {

console.log("Received answer from async code");

});

console.log("finishing sync code");


Related Topics



Leave a reply



Submit