Suppose that I am also astute enough to check the network activity panel of the browser. There I see the request in question (I can even ensure that it’s the correct one, because my favorite browser provides a stack trace for each request, though not yet for each websocket exchange). I confirm that it executed in 1.0546 seconds. But it took me several seconds to step from line (x – y) to line x, where the failure occurs under normal conditions. What is the state of the system (at least the relevant component(s) of that state) in that interval? Is it A or is it B? I can be a very fast clicker to find out using the debugger, or I can insert a console.log(…) at line x. Doing the latter causes the browser to spit out a navigable snapshot of a given datum that matches the state of the system at the moment the log statement executed. I can come back to it two days later and, assuming the computer is still running and the browser is still up, I can examine the now obsolete state. I can then make an informed decision on how to handle that state: whether to change it, provide a promise or other waiting mechanism, or something entirely different.
Depending on their content and use, print statements may be one example of what Andrew Hunt calls “tracer bullets” in The Pragmatic Programmer (49). Though not every debugging situation warrants the use of code-altering debug statements, in some cases they provide invaluable access to otherwise unreachable information about the inner state of a program. They should be among the trusted tools of any expert developer.