Recently, I met a wonderful problem in the process of following up the project, which made me think that my computer had broken down (of course, Ben Bai, who is not yet in the business, often thinks that the computer has broken down hahaha). But after a couple of hours and a lot of reading, I finally found the problem, which is the printing mechanism of js console. log.

“Bugs”

Of course, that’s just what I thought was a bug.

First, my project requirements are: so and so ~ so and so ~

And then finally, part of the interface that the project presents looks like this

I will enter the corresponding content in the corresponding input field, and then when I click ok, I will send an Ajax request to the server to add a message to the database.

A very common popover, but I wanted to see the data I sent before sending the request, so I printed the dynamically bound data before sending it, when the problem occurred. Let’s look at the code first

Js code

async addWords() {
  console.log(this.addWordsForm); // Prints data./ / to omit
  await API.getWordsAdd({ // This is the encapsulated AXIOS request method. }).then(res= >{... });this.addWordsDialog = false; // Close the new talk popover
  this.$refs.addWordsForm.resetFields(); // Reset the form. },Copy the code

I typed the following in the input box

And the data structure I envisioned would look like this, and THEN I would be able to make a normal request to add a voice

{ob: Observer}

  • wordsList: Array(3)

    • 0: “Talk 1”
    • 1: “Words 2”
    • 2: “Talking Skills 3”
    • 3: “Speech skill 4”
    • length: 4
  • WordsTitle: “Test Words”

But when I got the printed result, I felt very surprised, because: my data had “disappeared”!! Look at the picture,

Why is that? Where does my data start to go missing?

Screening “bug”

I wondered what was going wrong, so I first added a change event to the input field to print the contents of the form each time the content changes

html:

.<el-input
  v-model="addWordsFormCopy.wordsList[index]"
  autocomplete="off"
  @change="formChange()"// Add an event ></el-input>.Copy the code

Vuejs:

methods: {
  formChange(){
    console.log(this.addWordsForm); // Print the content of the new form}},Copy the code

When I typed two words, there was no doubt that he typed twice, because every time he typed, he typed again

At this point I want to expand to see if my input exists to determine if this is where my form content “disappears”

The data is still there, which means that the form content has not “disappeared” in this place.

Of course it is. At first I didn’t think there was anything wrong here, but the more I looked at it, the weirder it became, you know

Suddenly, I found a major problem, they print at different times, the first time to print the second message has not entered, why it has already printed out??

What’s the mechanism? The metaphysics? Js Bug? He prejudged me?

First guess

My first thought was, does js console.log have a delay mechanism? So I tested it again:

I typed one message, waited a while, typed a second message, and then looked at the two printouts,

Of course, the two prints look the same, which means that this delay mechanism, as I thought, doesn’t exist.

OK, this guess passes.

Second guess

This time I thought about what I had learned, and I thought that the console.log was the result of printing the last time. So if I typed it a hundred times, Is the result of the first print going to be the hundredth print?

No more words, directly open test, I first input a message, and then look at the print content

Of course, the form content is still there, and there is only one, as shown above

Then I closed the printed results, input the second and third sentences, and expand them all. I expected that they would print the same content for the three times, so that I could verify my second guess, but…..

The result is obvious. My guess seems to be wrong again. The content printed in the next two times is the expected final form result, but the content printed in the first time is still one.

I didn’t have any clue until now. I couldn’t find the answer in MDN’s explanation of console, so I had to turn to Baidu and Google for help. I checked some blog posts about the printing mechanism of console.log, and finally found the answer in the comments.

Seek the principle of console.log printing

In a section on the console in a book called javascript You Don’t Know:

There is no specification or set of requirements that specify how the console.* family of methods should work — they are not a formal part of JavaScript, but are added to it by the host environment (see the “Types and Syntax” section of this book). Therefore, different browsers and JavaScript environments can be implemented as they wish, which can sometimes lead to confusion.

In particular, under certain conditions, the console.log(..) of some browsers Incoming content is not output immediately. The main reason for this is that IN many programs (not just JavaScript), I/O is a very slow blocking part. So, (from a page /UI perspective) the browser can improve performance by asynchronously processing console I/O in the background, where the user may not even be aware of it happening.

Doesn’t feel like it’s easy to understand?

For a simple example, there is the following JS code

let foo = {
    bar: 1;
}
console.log(foo);
foo.bar++;
Copy the code

At this point, according to conventional thinking, we think that the print operation is before the increment operation, which means that the print result should be {bar: 1}, but in fact it is

I find it very strange that it shows {bar: 1} when printing, but when we expand the print, it shows {bar: 2}.

Under certain conditions, some browsers (remember, some browsers) do not print the output immediately, but rather asynchronously process console I/O in the background to improve performance. Console. log can be interpreted as “printing asynchronously” under certain conditions. Because there’s really nothing called asynchronous printing, just the process of printing feels asynchronous), and when the browser reads console.log(foo), the console immediately prints a snapshot of the reference

What is a snapshot that you can interpret as a call to json.stringify () once to show a sequence of objects as a string

It looks like the picture below

After printing a snapshot of the reference, he continues the execution line by lineconsole.log(foo)After the code, and after the encounterfoo.bar++The value of bar in the object foo increases to 2. When we expand the printed content, he will look for the referenced address to retrieve the content and show it to the user. So it gives the illusion of asynchronous printing, but in fact,console.log()It’s still a synchronous operation.

So theoretically, as long as you click on the console print before the browser reads the increment, it should still {bar: 1}. Let’s test that out

Let’s say our Chrome browser isn’t very good and it takes five seconds to read the next line of code:

let foo = {
  bar: 1
}
console.log(foo); // First print
setTimeout(() = > {
  console.log(foo); // Print the second time
  foo.bar++;
}, 5000);
Copy the code

What if we clicked on the first print in five seconds?

Sure enough, when we clicked on the print within five seconds, it was still {bar: 1} because the increment had not been performed, and on the second print five seconds later, it was exactly what we expected.

This seemingly asynchronous printing is limited not only by conditions (such as printing a reference data type) but also by browsers (older Versions of IE simply printed out the entire reference address). But in some ways, it’s an optimization for the browser

Why is it an optimization? If you want to print a lot of parameters in the reference address, and print a lot of times, countless times, the browser will become very expensive.

Checking the

Reworking the bug I encountered in the beginning, let’s get the code

async addWords() {
  console.log(this.addWordsForm); // Prints data./ / to omit
  await API.getWordsAdd({ // This is the encapsulated AXIOS request method. }).then(res= >{... });this.addWordsDialog = false; // Close the new talk popover
  this.$refs.addWordsForm.resetFields(); // Reset the form. },Copy the code

Why print out my content is “disappear”, because in the end, the code above again I to reset the form, content, means, when I clicked on the console print data, access to reference address is already in an empty form, then nature showed me the printout is empty ~

Let’s try printing the list of words from this list directly

console.log(this.addWordsForm.wordsList)
Copy the code

The result is exactly the same as the previous analysis, there is data in the referenced snapshot, but the data in the expanded final reference address has been reset, so it is empty string.

So there’s really no impact on the actual development, when I submit the data before resetting the form, it’s still there, it’s not “gone”.

To sum up:

  • When you use console printing during debugging and find that the printed reference snapshot does not match the expanded data, be aware that this is due to asynchrony of browser I/O
  • When you want to debug in this situation, it’s best to use the break point in the JavaScript debugger instead of relying entirely on the console, which can trick your eyes
  • What about interrupting a little bit? So let’s force print the snapshot and see what’s in the snapshot. What? How do I do it? As mentioned earlier, json.stringify (foo);