Why 0.1 + 0.2! = 0.3, please give reasons

Because JS uses IEEE 754 double precision version (64-bit), and as long as the use of IEEE 754 language has this problem.

We all know that the computer representation of the decimal system is in binary, so 0.1 is in binary

// (0011) indicates loop
0.1 = 2^4 - * 1.10011(0011)
Copy the code

So how do we get this binary, can we compute it

Decimals count in binary and integers differently. In multiplication, only the decimal place is counted, the integer bit is used as the binary for each bit, and the first digit is the highest digit. So we get 0.1 = 2^-4 * 1.10011(0011), so the calculation of 0.2 is basically as above, only need to remove the first step of multiplication, so we get 0.2 = 2^-3 * 1.10011(0011).

Back to IEEE 754 doubles. Of the 64 digits, the sign bit occupies one place, the integer bit occupies eleven places, and the other 52 places are all decimal places. Since both 0.1 and 0.2 are infinite loop binary, we need to determine whether to carry at the end of the decimal place (just like rounding in decimal).

So 2^-4 * 1.10011… After carrying 001, it becomes 2^-4 * 1.10011(0011 * 12 times)010. So adding these two binaries together yields 2^-2 * 1.0011(0011 * 11 times)0100, which in decimal is 0.30000000000000004

The native solution is shown in the following code

parseFloat((0.1 + 0.2).toFixed(10))
Copy the code

Ten Ajax requests are made simultaneously, all of them return a presentation result, and they are allowed to fail up to three times to say what the design is about

Promise.all is a function that returns if it fails once. This is a problem that needs to be solved. Here are two ways to do this

// The following is incomplete code, focusing on the idea of non-promise writing
let successCount = 0
let errorCount = 0
let datas = []
ajax(url, (res) => {
     if (success) {
         success++
         if (success + errorCount === 10) {
             console.log(datas)
         } else {
             datas.push(res.data)
         }
     } else {
         errorCount++
         if (errorCount > 3) {
            // If the number of failures is greater than 3, an error should be reported
             throw Error('Failed three times')}}})/ / Promise
let errorCount = 0
let p = new Promise((resolve, reject) = > {
    if (success) {
         resolve(res.data)
     } else {
         errorCount++
         if (errorCount > 3) {
            // If the number of failures is greater than 3, an error should be reported
            reject(error)
         } else {
             resolve(error)
         }
     }
})
Promise.all([p]).then(v= > {
  console.log(v);
});
Copy the code

Based on Localstorage design a 1M cache system, need to realize the cache elimination mechanism

The design idea is as follows:

  • Each stored object needs to add two properties: expiration time and storage time.
  • A property is used to store the current amount of space in the system, increasing the property with each storage. When the value of this attribute is greater than 1 MB, sort the data in the system by time and delete a certain amount of data to ensure that the required data can be stored.
  • When fetching data each time, check whether the cached data is expired. If it is expired, delete it.

The following is the code implementation, the implementation of the idea, but there may be bugs, but this kind of design problem is generally given the design idea and part of the code, do not need to write a problem-free code

class Store {
  constructor() {
    let store = localStorage.getItem('cache')
    if(! store) { store = {maxSize: 1024 * 1024.size: 0
      }
      this.store = store
    } else {
      this.store = JSON.parse(store)
    }
  }
  set(key, value, expire) {
    this.store[key] = {
      date: Date.now(),
      expire,
      value
    }
    let size = this.sizeOf(JSON.stringify(this.store[key]))
    if (this.store.maxSize < size + this.store.size) {
      console.log('over the -- -- -- -- -- -- -- -- -- -- --');
      var keys = Object.keys(this.store);
      // Time sequence
      keys = keys.sort((a, b) = > {
        let item1 = this.store[a], item2 = this.store[b];
        return item2.date - item1.date;
      });
      while (size + this.store.size > this.store.maxSize) {
        let index = keys[keys.length - 1]
        this.store.size -= this.sizeOf(JSON.stringify(this.store[index]))
        delete this.store[index]
      }
    }
    this.store.size += size

    localStorage.setItem('cache'.JSON.stringify(this.store))
  }
  get(key) {
    let d = this.store[key]
    if(! d) {console.log('This property cannot be found');
      return
    }
    if (d.expire > Date.now) {
      console.log('Expired delete');
      delete this.store[key]
      localStorage.setItem('cache'.JSON.stringify(this.store))
    } else {
      return d.value
    }
  }
  sizeOf(str, charset) {
    var total = 0,
      charCode,
      i,
      len;
    charset = charset ? charset.toLowerCase() : ' ';
    if (charset === 'utf-16' || charset === 'utf16') {
      for (i = 0, len = str.length; i < len; i++) {
        charCode = str.charCodeAt(i);
        if (charCode <= 0xffff) {
          total += 2;
        } else {
          total += 4; }}}else {
      for (i = 0, len = str.length; i < len; i++) {
        charCode = str.charCodeAt(i);
        if (charCode <= 0x007f) {
          total += 1;
        } else if (charCode <= 0x07ff) {
          total += 2;
        } else if (charCode <= 0xffff) {
          total += 3;
        } else {
          total += 4; }}}returntotal; }}Copy the code

Details about Event loop

It is well known that JS is a non-blocking single-threaded language, since it was originally created to interact with browsers. If JS is a multithreaded language, we might have problems dealing with DOM in multiple threads (adding nodes in one thread and removing nodes in another), but we can introduce read/write locks to solve this problem.

JS will generate execution environments during execution, which will be added to the execution stack sequentially. If asynchronous code is encountered, it is suspended and added to the Task queue (there are several tasks). Once the execution stack is empty, the Event Loop will take out the code to be executed from the Task queue and put it into the execution stack for execution, so essentially the asynchronous or synchronous behavior in JS.

console.log('script start');

setTimeout(function() {
  console.log('setTimeout');
}, 0);

console.log('script end');
Copy the code

The above code is asynchronous even though the setTimeout delay is 0. This is because the HTML5 standard states that the second argument to this function must be no less than 4 milliseconds, which is automatically incrementing. So setTimeout will still print after script end.

Different Task sources are allocated to different Task queues. Task sources can be divided into microTask and macroTask. In the ES6 specification, microTasks are called Jobs and macroTasks are called Tasks.

console.log('script start');

setTimeout(function() {
  console.log('setTimeout');
}, 0);

new Promise((resolve) = > {
    console.log('Promise')
    resolve()
}).then(function() {
  console.log('promise1');
}).then(function() {
  console.log('promise2');
});

console.log('script end');
// script start => Promise => script end => promise1 => promise2 => setTimeout
Copy the code

The setTimeout is written before the Promise, but since the Promise is a microtask and the setTimeout is a macro task, it will print.

Microtasks include process.nextTick, Promise, object. observe, and MutationObserver

Macro tasks include Script, setTimeout, setInterval, setImmediate, I/O, and UI Rendering

There is a misconception that microtasks are faster than macro tasks, but this is not true. Since a macro task includes a script, the browser will execute a macro task first and then execute the microtask if there is any asynchronous code.

So the correct sequence of an Event loop would look like this

  1. Execute the synchronization code, which is a macro task
  2. If the execution stack is empty, query whether microtasks need to be executed
  3. Perform all microtasks
  4. Render the UI if necessary
  5. The next Event loop is started, executing the asynchronous code in the macro task

According to the above Event loop order, if the asynchronous code in the macro task has a large number of calculations and needs to manipulate the DOM, we can put the manipulation DOM into the microtask for faster interface response.

Event loop in Node

The Event loop in Node is different from that in the browser.

Node’s Event loop is divided into six stages that are run repeatedly in sequence

┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ ┌ ─ > │ timers │ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ │ I/O callbacks │ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ │ idle, Prepare │ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ incoming: │ │ │ poll │ < ─ ─ connections ─ ─ ─ │ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ data, Etc. │ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ │ check │ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┬ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┴ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ └ ─ ─ ┤ close callbacks │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘Copy the code
timer

The timers phase executes setTimeout and setInterval

The time specified by a timer is not the exact time; instead, the callback is executed as soon as possible after that time, which may be delayed because the system is executing other transactions.

The lower limit has a range of time: [1, 2147483647]. If the time set is not in this range, it will be set to 1.

I/O

The I/O phase performs callbacks except for the close event, timer, and setImmediate

idle, prepare

Idle, prepare phase internal implementation

poll

The poll phase is important, because in this phase, the system is doing two things

  1. Execute the to-point timer
  2. Execute events in the poll queue

And when there’s no timer in the poll, two things happen

  • If the poll queue is not empty, the callback queue is traversed and executed synchronously until the queue is empty or the system is limited
  • If the poll queue is empty, two things happen
    • If you havesetImmediateIf you need to execute, the poll phase will stop and go to the check phasesetImmediate
    • If there is nosetImmediateNeeds to execute, waits for the callback to be queued and executes the callback immediately

If another timer needs to be executed, the callback is performed back in the timer phase.

check

The check phase performs setImmediate

close callbacks

The Close Callbacks phase executes the close event

And in Node, in some cases the timer execution sequence is random

setTimeout((a)= > {
    console.log('setTimeout');
}, 0);
setImmediate((a)= > {
    console.log('setImmediate');
})
// This might output setTimeout, setImmediate
// May also output the opposite, depending on performance
// Because it might take less than a millisecond to get to the event loop, setImmediate is performed
// Otherwise, setTimeout is executed
Copy the code

In this case, of course, the order of execution is the same

var fs = require('fs')

fs.readFile(__filename, () => {
    setTimeout((a)= > {
        console.log('timeout');
    }, 0);
    setImmediate((a)= > {
        console.log('immediate');
    });
});
// Because the callback to readFile is performed in poll
// setImmediate is discovered, so it immediately jumps to the check phase to perform a callback
// Run setTimeout in the timer phase
// So the output must be setImmediate, setTimeout
Copy the code

Macrotask execution is described above, and microTask is executed immediately after each of these phases.

setTimeout((a)= >{
    console.log('timer1')

    Promise.resolve().then(function() {
        console.log('promise1')})},0)

setTimeout((a)= >{
    console.log('timer2')

    Promise.resolve().then(function() {
        console.log('promise2')})},0)

// The above code prints differently in the browser and node
// Print timer1, timer2, timer2, and timer2 in the browser
// Node prints timer1, timer2, promise1, promise2
Copy the code

Process. nextTick in Node executes before any other microTask.

setTimeout((a)= > {
  console.log("timer1");

  Promise.resolve().then(function() {
    console.log("promise1");
  });
}, 0);

process.nextTick((a)= > {
  console.log("nextTick");
});
// nextTick, timer1, promise1
Copy the code

Finally attached my public number