Transcript from the "Console Log & Process stdout" Lesson
[00:00:16] And then we will progressively move to bigger and bigger things, like listening for web requests, and serving up files, and those other more exciting things. But we're gonna start there, and we're actually gonna start even deeper than that in the stack. So to dive in, I want to make sure you all have ex1.js from your exercises folder open.
[00:00:37] And you should see that there is literally nothing but a code comment here. We're gonna write our very first Node program. If you're listening to this, and you've never written a Node program? We're about to fix that, cuz you're about to write your first Node program. So before we can even write a Node program, we have to ask ourselves the question.
[00:00:55] How am I going to get information into my program, in some way? But even more importantly than that, how am I gonna get information out? And I said that Node is all about I/O, Node is all about modeling I/O in an efficient way. Which means we need to understand I/O at a deeper level than you're perhaps used to.
[00:01:40] A lot of us just take that stuff for granted. But if you're going to write Node, and do so well, and use the system to its most advantage, and be most efficient with it? I think you need to understand something about how Node connects to the environment around it.
[00:01:57] So we're gonna go even deeper than just an API, console.log. We need to understand a standard for I/O and system integration, system- level task calling, called POSIX, P-O-S-I-X. You Google around for that, you'll find a lot of information about that. I'm sure I'm not even gonna be completely accurate on it, because I'm not a Unix programmer from the 1970s.
[00:02:22] But I can tell you that POSIX is essentially the way that C-style programs integrate with Linux-style operating systems. So when we talk about POSIX, one of the major things that we're talking about is the standard I/O subsystem. Standard I/O is a set of three streams that model input and output to a program.
[00:02:47] If you've ever written a C program, for example, you might remember reading and writing from file descriptors like 0, 1, and 2. That's the POSIX subsystem being exposed to your C program by way of these file identifiers, or file descriptors. 0 being stdout, 1 being stdin, I'm sorry, 0 being stdin, 1 being stdout, and 2 being stderr.
[00:03:14] So we have these file descriptors that reference input, output, and a second channel of output called error. And we're not writing C programs here, so it's not particularly interesting, how you do it in a C program. But how would we access, at a fundamental level, those I/O streams that are exposed to our Node program?.
[00:03:34] Cuz Node made the choice to expose most of the system connections through a POSIX-like interface. So it'll be familiar to you, if you've ever done POSIX-style programming in some other language, you'll find bits of familiarity here. How do we access those, well, the main touchpoint that we're gonna access them on is called process.
[00:04:48] And light bulbs, and refrigerators, and TVs, and computers, and every other kind of device that you could imagine. Because the language doesn't make very many assumptions about how communication's gonna happen. It's up to the environment to decide that. Since Node was originally designed, and likely going to be run in those POSIX environments?
[00:05:09] It makes an awful lot of sense that they would choose to expose the I/O to our programs through that POSIX interface. In the browser, that's not a POSIX environment, you're not dealing with processes, and process IDs, and that kind of stuff. So when you do console.log, and it's running in the browser, the browser chose to expose it in a different way.
[00:05:28] It's just a direct connection over to the developer tools, right? And that's a difference based on the hosting environment exposing the I/O. So we're in Node here, and we've been exposed to POSIX interface, and we said there are standard I/O streams. So we can say process.stdout to access the stream for standard output.
[00:05:52] And streams are a first-class citizen, they are an actual kind of data structure in Node that we're gonna get real familiar with, as we go throughout today. So the first exposure that we have to it is, if you want to write some content to a stream, you call .write.
[00:06:11] So we could have said console.log, or we could have said process.stdout.write. Now, I'm gonna have both of these here, but let's first run the console.log statement, comment out the stdout for just a moment. Either one of these, they're gonna do something very similar, which is send some output.
[00:06:30] Switch over to your console, and run node ex1.js, and you see the hello world being printed out. Now, let's go back to that code, and let's comment out the stdout.write version of it, and then rerun it. Okay, so we want to compare, what happened when I did console.log, and what happens when I do process.stdout.write?
[00:06:57] And if we run this differently, you should notice a slight difference, which is that the process.stdout.write output did not include a trailing newline. Which is why it ended up right here, just continuing my command line prompt right after that, there was no trailing newline. So one way you might think of it is, you might think of it as console.log is a wrapper around process.stdout.write that throws on a trailing newline.
[00:07:27] That’s not actually accurate, the console.log is doing other things, many other things besides simply calling process.stdout.write. But in the case of a very simple string, we could sort of think of it in that way. It's kind of the equivalent of if we'd put a trailing newline in our output, before sending it out.
[00:07:49] The reason why I wanna make the point about accessing these output streams in a very bare way, is because the whole point of Node using this asynchronous I/O model is so that I/O can happen as efficiently as possible. And the least possible efficient way for I/O to happen is for you to print a character string directly to some stream.
[00:08:13] So process.stdout.write, with a character string like that, is the least efficient way for that to possibly happen. Because it has to go through some layers of translation to end at the host environment, that is, our shell environment that we're running things in, and actually end up getting printed.
[00:08:31] We don't see that detail, but there's layers of abstraction where that stuff is being converted or whatever. So if we were to, for example, have a stream that already had the binary bits of information for the hello world string in it, and we did .write with a binary stream, with a buffer?
[00:08:50] Then it would go much more efficiently, directly through those subsystems, and then still get printed out as characters in our shell, okay? So console.log is a developer convenience, but under the covers, there's a lot more detail about how things are going. And as we get later into our exercises, we're gonna want to be able to efficiently write lots of information to a stream, even something like stdout.
[00:09:11] And we're going to want to be doing so at the at the sort of binary buffer level, rather than in encoded character strings, okay? So that's our first kind of lesson here, is that we're dealing with standard output, that's kind of wrapped in a developer convenience called console.log