Interviewer: What about data types in JavaScript? Storage differences?

preface

In JavaScript, we can divide into two types:

  • Basic types of
  • The complex type

The difference between the two types is that the storage location is different

First, basic types

The basic types are as follows:

  • Number
  • String
  • Boolean
  • Undefined
  • null
  • symbol

Number

The most common integer type format for numeric values is decimal. Octal (starting with zero) and hexadecimal (starting with 0x) can also be set

let intNum = 55 // Base 10 of 55
let num1 = 070 // Base 56
let hexNum1 = 0xA // hexadecimal 10
Copy the code

Floating-point types must contain a decimal point in the sum of values and can also be represented by scientific notation

let floatNum1 = 1.1;
let floatNum2 = 0.1;
let floatNum3 = 1.; // Valid, but not recommended
let floatNum = 3.125 e7; / / equal to 31250000
Copy the code

Within numeric types, there is a special value NaN, which means “not a number” and is used to indicate that the operation that was supposed to return a number failed (rather than throw an error)

console.log(0/0); // NaN
console.log(-0/+0); // NaN
Copy the code

Undefined

Undefined has only one value, which is the special value Undefined. When a variable is declared using var or let but not initialized, it is equivalent to giving the variable a undefined value

let message;
console.log(message == undefined); // true
Copy the code

There is a difference between a variable that contains undefined and an undefined variable

let message; // This variable is declared with undefined

console.log(message); // "undefined"
console.log(age); // This variable is not declared
Copy the code

String

Strings can be marked with double quotation marks (“), single quotation marks (‘), or backquotation marks (‘)

let firstName = "John";
let lastName = 'Jacob';
let lastName = `Jingleheimerschmidt`
Copy the code

Strings are immutable, meaning that their values cannot change once they are created

let lang = "Java";
lang = lang + "Script";  // Destroy before create
Copy the code

Null

The Null type also has only one value, the special value Null

Logically, a null value represents an empty object pointer, which is why passing a NULL to typeof returns “object”

let car = null;
console.log(typeof car); // "object"
Copy the code

Undefined is derived from null

console.log(null= =undefined); // true
Copy the code

Whenever a variable wants to hold an object and there is no object available at the time, null can be used to populate the variable

Boolean

Boolean types have two literals: true and false

Boolean allows you to convert other types of data to Booleans

Here are the rules:

The data type is converted totrueThe value of is converted tofalseThe value of theStringNon-empty string"" 
 NumberNon-zero values (including infinity)0NaN 
 ObjectAny objectnullUndefined N/A (not present)undefined
Copy the code

Symbol

The Symbol is a primitive value and the Symbol instance is unique and immutable. The purpose of the symbol is to ensure that object attributes are uniquely identified without the risk of attribute collisions

let genericSymbol = Symbol(a);let otherGenericSymbol = Symbol(a);console.log(genericSymbol == otherGenericSymbol); // false

let fooSymbol = Symbol('foo');
let otherFooSymbol = Symbol('foo');
console.log(fooSymbol == otherFooSymbol); // false
Copy the code

2. Reference types

Complex types are collectively called Object, and we mainly describe the following three types:

  • Object
  • Array
  • Function

Object

The common way to create an object is the literal representation of an object. The property name can be a string or a value

let person = {
    name: "Nicholas"."age": 29.5: true
};
Copy the code

Array

A JavaScript array is an ordered set of data, but unlike other languages, each slot in the array can store any type of data. Also, arrays are dynamically sized, growing automatically as data is added

let colors = ["red".2, {age: 20 }]
colors.push(2)
Copy the code

Function

Functions are actually objects, and each Function is an instance of Function, which also has properties and methods, just like any other reference type

There are three common expressions for functions:

  • Function declaration
// Function declaration
function sum (num1, num2) {
    return num1 + num2;
}
Copy the code
  • Functional expression
let sum = function(num1, num2) {
    return num1 + num2;
};
Copy the code
  • Arrow function

Function declaration and function expression in two ways

let sum = (num1, num2) = > {
    return num1 + num2;
};
Copy the code

Other reference types

In addition to the three mentioned above, there are also Date, RegExp, Map, Set, etc……

Three, storage differences

Base data types and reference data types are stored differently in memory:

  • Basic data types are stored in stacks

  • Objects of reference type are stored in the heap

When we assign a variable to a variable, the first thing the parser needs to determine is whether the value is a primitive or a reference type

Here’s an example

Basic types of

let a = 10;
let b = a; // Assign
b = 20;
console.log(a); / / 10 values
Copy the code

The value of A is a basic type. It is stored on the stack, and the value of A is assigned to B. Although the two variables have the same value, the two variables hold two different memory addresses

The following illustration illustrates the basic type assignment:

Reference types

var obj1 = {}
var obj2 = obj1;
obj2.name = "Xxx";
console.log(obj1.name); // xxx
Copy the code

Reference type data is stored internally, and each heap memory has a reference address, which is stored on the stack

Obj1 is a reference type, summarized during assignment, which actually copies the stack reference address of the heap object to obj2. In fact, they all refer to the same heap object, so changing obj2 will affect obj1

The following figure illustrates the reference type assignment process

summary

  • Different allocation of memory addresses when declaring variables:
    • Values of simple types are stored on the stack, and the corresponding values are stored on the stack
    • The value corresponding to the reference type is stored in the heap, and the address pointing to the heap memory is stored in the stack
  • Different types of data result in different variable assignments:
    • Simple type assignment, which generates the same value, two objects corresponding to different addresses
    • Complex type assignment assigns the memory address of an object to another variable. That is, two variables refer to the same object in heap memory

    Interviewer: What’s the difference between deep copy and shallow copy? How to implement a deep copy?

Data type storage

As mentioned in the previous article, there are two main data types in JavaScript:

  • Basic types of
  • Reference types

Basic type data is stored in stack memory

Reference type data is stored in heap memory, and the variable of a reference data type is a reference to an actual object in heap memory, which is stored on the stack

Shallow copy

Shallow copy refers to the creation of new data that has an exact copy of the original data attribute values

If the property is of a primitive type, the value of the primitive type is copied. If the attribute is a reference type, the memory address is copied

That is, shallow copies copy a layer, and deep reference types share memory addresses

Let’s simply implement a shallow copy

function shallowClone(obj) {
    const newObj = {};
    for(let prop in obj) {
        if(obj.hasOwnProperty(prop)){ newObj[prop] = obj[prop]; }}return newObj;
}
Copy the code

In JavaScript, shallow copies exist:

  • Object.assign
  • Array.prototype.slice().Array.prototype.concat()
  • Replication using extended operators

Object.assign

var obj = {
    age: 18.nature: ['smart'.'good'].names: {
        name1: 'fx'.name2: 'xka'
    },
    love: function () {
        console.log('fx is a great girl')}}var newObj = Object.assign({}, fxObj);
Copy the code

slice()

const fxArr = ["One"."Two"."Three"]
const fxArrs = fxArr.slice(0)
fxArrs[1] = "love";
console.log(fxArr) // ["One", "Two", "Three"]
console.log(fxArrs) // ["One", "love", "Three"]
Copy the code

concat()

const fxArr = ["One"."Two"."Three"]
const fxArrs = fxArr.concat()
fxArrs[1] = "love";
console.log(fxArr) // ["One", "Two", "Three"]
console.log(fxArrs) // ["One", "love", "Three"]
Copy the code

Extended operator

const fxArr = ["One"."Two"."Three"]
const fxArrs = [...fxArr]
fxArrs[1] = "love";
console.log(fxArr) // ["One", "Two", "Three"]
console.log(fxArrs) // ["One", "love", "Three"]
Copy the code

Deep copy

Deep copy opens up a new stack. Two object families are identical but correspond to two different addresses. Modifying the properties of one object does not change the properties of the other

Common deep-copy modes are:

  • _.cloneDeep()

  • jQuery.extend()

  • JSON.stringify()

  • Handwriting loop recursion

_.cloneDeep()

const _ = require('lodash');
const obj1 = {
    a: 1.b: { f: { g: 1}},c: [1.2.3]};const obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);// false
Copy the code

jQuery.extend()

const$=require('jquery');
const obj1 = {
    a: 1.b: { f: { g: 1}},c: [1.2.3]};const obj2 = $.extend(true, {}, obj1);
console.log(obj1.b.f === obj2.b.f); // false
Copy the code

JSON.stringify()

const obj2=JSON.parse(JSON.stringify(obj1));
Copy the code

However, this approach has the disadvantage of ignoring undefined, symbol, and functions

const obj = {
    name: 'A'.name1: undefined.name3: function() {},
    name4:  Symbol('A')}const obj2 = JSON.parse(JSON.stringify(obj));
console.log(obj2); // {name: "A"}
Copy the code

Cycle recursion

function deepClone(obj, hash = new WeakMap(a)) {
  if (obj === null) return obj; // If it is null or undefined, I will not copy it
  if (obj instanceof Date) return new Date(obj);
  if (obj instanceof RegExp) return new RegExp(obj);
  // May be objects or ordinary values if functions do not need deep copies
  if (typeofobj ! = ="object") return obj;
  // Make a deep copy of an object
  if (hash.get(obj)) return hash.get(obj);
  let cloneObj = new obj.constructor();
  // Find the constructor from the parent class stereotype, which points to the current class itself
  hash.set(obj, cloneObj);
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      // Implement a recursive copycloneObj[key] = deepClone(obj[key], hash); }}return cloneObj;
}
Copy the code

Fourth, the difference between

Here are two images to help you see the difference between a shallow copy and a deep copy

As you can see from the above figure, both shallow and deep copies create a new object, but behave differently when copying object properties

Shallow copy only copies the pointer that the attribute points to an object, but not the object itself. The old and new objects still share the same memory. Modifying object attributes affects the original object

/ / shallow copy
const obj1 = {
    name : 'init'.arr : [1[2.3].4]};const obj3=shallowClone(obj1) // a shallow copy method
obj3.name = "update";
obj3.arr[1] = [5.6.7];// Old and new objects still share the same memory

console.log('obj1',obj1) // obj1 { name: 'init', arr: [ 1, [ 5, 6, 7 ], 4 ] }
console.log('obj3',obj3) // obj3 { name: 'update', arr: [ 1, [ 5, 6, 7 ], 4 ] }
Copy the code

But deep copy will create another identical object, the new object and the original object do not share memory, modify the new object will not change to the original object

/ / copy
const obj1 = {
    name : 'init'.arr : [1[2.3].4]};const obj4=deepClone(obj1) // a deep-copy method
obj4.name = "update";
obj4.arr[1] = [5.6.7];// The new object does not share memory with the original object

console.log('obj1',obj1) // obj1 { name: 'init', arr: [ 1, [ 2, 3 ], 4 ] }
console.log('obj4',obj4) // obj4 { name: 'update', arr: [ 1, [ 5, 6, 7 ], 4 ] }
Copy the code

summary

If the copy type is reference type:

  • A shallow copy is a copy of a layer. If the property is an object, the shallow copy is a copy. The two objects point to the same address

  • Deep copy is a deep recursive copy. If the property is an object, the deep copy is a new stack, and the two objects point to different addresses

Interviewer: What are anti-shake and throttling? What’s the difference? How to do that?

What is it

Essentially a means of optimizing code for high frequency execution

For example, when events such as resize, Scroll, Keypress, and Mousemove of the browser are triggered, the callback function bound to the event is constantly invoked, which greatly wastes resources and reduces front-end performance

To optimize the experience, we need to limit the number of calls to these events, which we can throttle and debounce to reduce

define

  • Throttling: runs only once within n seconds. If it is triggered repeatedly within N seconds, it takes effect only once
  • Anti-shake: this event will be executed after n seconds. If it is triggered repeatedly within N seconds, the timer will be reset

A classic metaphor:

Imagine the elevator under your building every day. The elevator completes a delivery, analogous to the execution and response of a function

Assume that the elevator has two running policies, Debounce and Throttle, with timeouts set to 15 seconds regardless of capacity constraints

The first person in the elevator comes in, 15 seconds after the exact delivery, this is throttling

Wait 15 seconds after the first person enters the elevator. If someone else comes in during the process, wait for 15 seconds to reset the timer until shipping begins after 15 seconds. This is anti-shake

Code implementation

The throttle

Throttling can be done using a timestamp and timer notation

With timestamps, the event is executed immediately and there is no way to execute again after the trigger is stopped

function throttled1(fn, delay = 500) {
    let oldtime = Date.now()
    return function (. args) {
        let newtime = Date.now()
        if (newtime - oldtime >= delay) {
            fn.apply(null, args)
            oldtime = Date.now()
        }
    }
}

Copy the code

If the timer is used, the timer is executed for the first time after the delay is milliseconds, and the timer is executed again after the event is stopped for the second time

function throttled2(fn, delay = 500) {
    let timer = null
    return function (. args) {
        if(! timer) { timer =setTimeout(() = > {
                fn.apply(this, args)
                timer = null}, delay); }}}Copy the code

You can combine the features of timestamp writing with those of timer writing to achieve a more precise throttling. To achieve the following

function throttled(fn, delay) {
    let timer = null
    let starttime = Date.now()
    return function () {
        let curTime = Date.now() // The current time
        let remaining = delay - (curTime - starttime)  // How much extra time is left since last time
        let context = this
        let args = arguments
        clearTimeout(timer)
        if (remaining <= 0) {
            fn.apply(context, args)
            starttime = Date.now()
        } else {
            timer = setTimeout(fn, remaining); }}}Copy the code

Image stabilization

Simple version of the implementation

function debounce(func, wait) {
    let timeout;

    return function () {
        let context = this; // Save this point
        let args = arguments; // Get the event object

        clearTimeout(timeout)
        timeout = setTimeout(function(){ func.apply(context, args) }, wait); }}Copy the code

If you need to perform the anti-shake function immediately, you can add the third parameter for judgment. The implementation is as follows:

function debounce(func, wait, immediate) {

    let timeout;

    return function () {
        let context = this;
        let args = arguments;

        if (timeout) clearTimeout(timeout); // timeout is not null
        if (immediate) {
            letcallNow = ! timeout;// It will be executed immediately for the first time, and will be triggered again only after the event is executed
            timeout = setTimeout(function () {
                timeout = null;
            }, wait)
            if (callNow) {
                func.apply(context, args)
            }
        }
        else {
            timeout = setTimeout(function () { func.apply(context, args) }, wait); }}}Copy the code

Second, the difference between

Similarities:

  • Both can be usedsetTimeoutimplementation
  • The goal is to reduce the frequency of callbacks. Saving computing Resources

Difference:

  • Function stabilization, at the end of a sequence of operations, to handle the callback, usingclearTimeout andsetTimeoutThe implementation. Function throttling, used to improve performance during high frequency events that are executed only once at a time during a continuous operation
  • Function chattering focuses on events that are triggered consecuentially over a period of time and are executed only once last, whereas function throttling is executed only once over a period of time

For example, both sets the time frequency to 500ms, triggers functions frequently, throttles frequently, and executes every 500ms over a 2-second period. Anti-shake, no matter how many times the method is deployed, after 2s, it will only be executed once

As shown below:

Three, application scenarios

Shudder in successive events that only need to trigger a callback scenario are:

  • The search box searches for input. The user only needs to type one last time before sending the request
  • Mobile phone number, email verification input detection
  • The window sizeresize. Just after the window adjustment is complete, calculate the window size. Prevent repeated rendering.

Throttling performs a callback at intervals in the following scenarios:

  • Scroll to load, load more or roll bottom to listen
  • Search box, search associative functions

Interviewer: What are some common DOM operations?

A, DOM

The Document Object Model (DOM) is a programming interface for HTML and XML documents

It provides a structured representation of a document and defines a way for that structure to be accessed from within the program to change the structure, style, and content of the document

Any HTML or XML document can be represented in the DOM as a hierarchy of nodes

Nodes are divided into many types, each of which corresponds to different information and/or tags in the document and has its own different features, data, and methods, and has some relationship with other types, as shown below:

<html>
    <head>
        <title>Page</title>
    </head>
    <body>
        <p>Hello World! </p ></body>
</html>
Copy the code

Just as DOM atoms contain subatomic particles, there are many types of DOM nodes that contain other types of nodes. Let’s take a look at three of them:

<div>
    <p title="title">
        content
    </p >
</div>
Copy the code

In the above structure, div and P are element nodes, Content is text node, and title is attribute node

Second, the operating

In daily front-end development, we all need DOM manipulation

In the past, we used Jquery, Zepto and other libraries to manipulate the DOM. Later, with the advent of vue, Angular, React and other frameworks, we controlled the DOM by manipulating data (mostly), and less and less by manipulating the DOM directly

But that doesn’t mean native operations aren’t important. Instead, DOM manipulation helps us understand the underlying content of the framework

The following is an analysis of common DOM operations, which are mainly divided into:

  • Create a node
  • Query node
  • Update the node
  • Add a node
  • Remove nodes

Create a node

createElement

Creates a new element that takes a parameter, the name of the tag to create the element

const divEl = document.createElement("div");
Copy the code

createTextNode

Create a text node

const textEl = document.createTextNode("content");
Copy the code

createDocumentFragment

To create a document shard, which represents a lightweight document that is primarily used to store temporary nodes, and then adds the contents of the document shard to the DOM at once

const fragment = document.createDocumentFragment();
Copy the code

When a DocumentFragment node is requested to be inserted into the document tree, instead of the DocumentFragment itself, all of its descendants are inserted

createAttribute

Create a property node, which can be a custom property

const dataAttribute = document.createAttribute('custom');
consle.log(dataAttribute);
Copy the code

Access to the node

querySelector

Pass in any valid CSS selector to select a single DOM element (first) :

document.querySelector('.element')
document.querySelector('#element')
document.querySelector('div')
document.querySelector('[name="username"]')
document.querySelector('div + p > span')
Copy the code

Returns null if there is no specified element on the page

querySelectorAll

Returns a list of all matching Element nodes in the node tree, or an empty node list if none matches

const notLive = document.querySelectorAll("p");
Copy the code

It is important to note that this method returns a static instance of NodeList, which is a static “snapshot” rather than a “live” query

There are other ways to get DOM elements, but I won’t cover them all

document.getElementById('ID attribute value'); Returns a reference to the object with the specified IDdocument.getElementsByClassName('Class attribute value'); Returns owning the specifiedclassObject collection ofdocument.getElementsByTagName(' tag name '); Returns a collection of objects with the specified label namedocument.getElementsByName('Name attribute value'); Returns the combination of objects with the specified namedocument/element.querySelector('CSS selector '); Only the first matched element is returneddocument/element.querySelectorAll('CSS selector '); Returns all matched elementsdocument.documentElement; Gets the HTML tag from the pagedocument.body; Gets the BODY tag on the pagedocument.all[' ']; Gets the object collection type of all element nodes in the pageCopy the code

In addition, each DOM element also has parentNode, childNodes, firstChild, lastChild, nextSibling, and previousSibling attributes, as shown below

Update the node

innerHTML

Not only can you modify the text content of a DOM node, you can also modify the subtree inside the DOM node directly through HTML fragments

...

var p = document.getElementById('p'); // Set the text to ABC: p.innerHTML = 'ABC'; // <p id="p">ABC</p > / / set the HTML: p.innerHTML = 'ABC <span style="color:red">RED</span> XYZ'; //

... The internal structure of

has been modified
Copy the code

The innerText, textContent

Automatically htML-encode strings, ensuring that no HTML tags can be set

<p id="p-id">... </p > var p = document.getElementById('p-id'); / / set the text: p.i nnerText = '< script > alert (" Hi ") < / script >'; <p id="p-id">&lt; // <p id="p-id">&lt; script&gt; alert("Hi")&lt; /script&gt; </p >Copy the code

The difference is that when reading an attribute, innerText does not return the text of the hidden element, whereas textContent returns all of the text

style

The STYLE property of a DOM node corresponds to all CSS and can be obtained or set directly. Encountered – Needs to be converted to a hump name

...

const p = document.getElementById('p-id'); / / set the CSS: p.style.color = '#ff0000'; p.style.fontSize = '20px'; // Name the hump p.style.paddingTop = '2em'; Copy the code

Add a node

innerHTML

If the DOM node is empty, for example,

, then you can modify the content of the DOM node by using innerHTML = ‘< SPAN >child’, equivalent to adding a new DOM node

If the DOM node is not empty, you can’t do this because innerHTML simply replaces all of the original child nodes

appendChild

Adds a child node to the last child node of the parent node

For example

<! - HTML structure - > JavaScript < p id = "JavaScript" > < / p > < div id = "list" > < p id = "Java" > Java < / p > < p id = "python" > python < / p > < p id="scheme">Scheme</p > </div>Copy the code

Add a p element

const js = document.getElementById('js')
js.innerHTML = "JavaScript"
const list = document.getElementById('list');
list.appendChild(js);
Copy the code

Now the HTML structure becomes the following

<! - HTML structure -- -- > < div id = "list" > < p id = "Java" > Java < / p > < p id = "python" > python < / p > < p id = "scheme" > scheme < / p > < p id="js">JavaScript</p > <! </div>Copy the code

In the above code, we add the DOM element after obtaining it. The JS node already exists in the current document tree. Therefore, the node will be deleted from its original position and then inserted into the new position

If new nodes are added dynamically, a new node is created and then inserted into the specified location

const list = document.getElementById('list'),
const haskell = document.createElement('p');
haskell.id = 'haskell';
haskell.innerText = 'Haskell';
list.appendChild(haskell);
Copy the code

insertBefore

Insert the child node at the specified position as follows:

parentElement.insertBefore(newElement, referenceElement)
Copy the code

Child nodes are inserted before the referenceElement

setAttribute

Adds an attribute node to the specified element, changing the value of the attribute if the element already exists

const div = document.getElementById('id')
div.setAttribute('class'.'white');// The first parameter attribute name, the second parameter attribute value.
Copy the code

Remove nodes

To remove a node, first get the node itself and its parent, and then call removeChild on the parent node to remove itself

// Get the node to be deleted:
const self = document.getElementById('to-be-removed');
// Get the parent node:
const parent = self.parentElement;
/ / remove:
const removed = parent.removeChild(self);
removed === self; // true
Copy the code

The deleted node is no longer in the document tree, but it is still in memory and can be added to another location at any time

A link to the

Developer.mozilla.org/zh-CN/docs/…

Interviewer: Tell me about your understanding of the cycle of events

What is it

First of all, JavaScript is a single-threaded language, which means you can only do one thing at a time, but that doesn’t mean that single-threaded is blocking, and the way to achieve single-threaded non-blocking is event loops

In JavaScript, all tasks can be categorized

  • Synchronization task: The synchronization task is executed immediately. The synchronization task is usually directly executed in the main thread

  • Asynchronous tasks: Tasks that are executed asynchronously, such as Ajax web requests, setTimeout timing functions, etc

The operation flow of synchronous and asynchronous tasks is as follows:

As can be seen from the above, synchronous tasks enter the main thread, that is, the main execution stack, while asynchronous tasks enter the task queue. When the execution of tasks in the main thread is empty, the corresponding tasks will be read from the task queue and pushed to the main thread for execution. The repetition of the above process creates a cycle of events

Macro task and micro task

If dividing tasks into synchronous and asynchronous tasks is not accurate, here is an example:

console.log(1)

setTimeout(() = >{
    console.log(2)},0)

new Promise((resolve, reject) = >{
    console.log('new Promise')
    resolve()
}).then(() = >{
    console.log('then')})console.log(3)
Copy the code

If we follow the above flowchart to analyze the code, we get the following execution steps:

  • console.log(1) , the synchronization task is executed in the main thread
  • setTimeout(), asynchronous task, intoEvent Table, 0 milliseconds laterconsole.log(2) The callback pushEvent Queue
  • new Promise, synchronization task, main thread directly executed
  • .then, asynchronous task, intoEvent Table
  • console.log(3), synchronization task, main thread execution

So the result should be 1 => ‘new Promise’ => 3 => 2 => ‘then’

But the actual result is: 1=>’new Promise’=> 3 =>’ then’ => 2

The reason for the divergence is the order in which asynchronous tasks are executed. The event queue is actually a “first in, first out” data structure, and the first events are read by the main thread first

In this example, the setTimeout callback event is queued first and should be executed before.then, but the result is the opposite

The reason is that asynchronous tasks can be subdivided into microtasks and macro tasks

Micro tasks

A function that needs to be executed asynchronously, after the execution of the main function and before the completion of the current macro task

Common microtasks include:

  • Promise.then

  • MutaionObserver

  • Object. Observe (obsolete; Proxy object substitution)

  • Process. NextTick (Node. Js)

Macro task

The time granularity of macro tasks is relatively large, and the time interval of execution cannot be precisely controlled, which does not meet the requirements of high real-time performance

Common macro tasks are:

  • Script (can be understood as the outer synchronization code)
  • setTimeout/setInterval
  • The UI rendering/UI events
  • PostMessage, MessageChannel
  • SetImmediate, I/O (Node.js)

At this point, the relationship between event loops, macro tasks, and micro tasks is shown in the figure below

According to this process, its execution mechanism is:

  • Execute a macro task, and if it encounters a microtask, place it in the event queue of the microtask
  • After the current macro task is executed, the event queue of the microtask is viewed and all the microtasks in it are executed in sequence

Let’s go back to the problem above

console.log(1)
setTimeout(() = >{
    console.log(2)},0)
new Promise((resolve, reject) = >{
    console.log('new Promise')
    resolve()
}).then(() = >{
    console.log('then')})console.log(3)
Copy the code

The process is as follows

// If console.log(1) is encountered, print 1
// When a timer is encountered, it belongs to a new macro task, which is reserved for later execution
// When you encounter a new Promise, this is implemented directly, print 'new Promise'
//. Then belongs to the microtask, which is put into the microtask queue and executed later
// If console.log(3) is encountered, print 3 directly
// Now go to the list of microtasks to see if there are any microtasks, find the. Then callback, execute it, print 'then'
// When the first macro task is completed, the next macro task is executed. There is only one timer macro task left
Copy the code

Async and await

‘async’ means’ async ‘and’ await ‘means’ async wait’. So async is used to declare an asynchronous method and await is used to wait for an asynchronous method to execute

async

The async function returns a Promise object. The following two methods are equivalent

function f() {
    return Promise.resolve('TEST');
}

// asyncF is equivalent to f!
async function asyncF() {
    return 'TEST';
}
Copy the code

await

Normally, the await command is followed by a Promise object that returns the result of that object. If it is not a Promise object, the corresponding value is returned

async function f(){
    / / is equivalent to
    // return 123
    return await 123
}
f().then(v= > console.log(v)) / / 123
Copy the code

“Await” blocks the following code regardless of what is followed by “await”

async function fn1 (){
    console.log(1)
    await fn2()
    console.log(2) / / blocking
}

async function fn2 (){
    console.log('fn2')
}

fn1()
console.log(3)
Copy the code

In the example above, await will block the following code (i.e. join microtask queue), execute the synchronous code outside async, finish the synchronous code, return to async function, and execute the blocked code

So the above output results are: 1, fn2, 3,2

Iv. Process analysis

From the above, we have a general idea of the order in which JavaScript executes various scenarios

Here’s the code:

async function async1() {
    console.log('async1 start')
    await async2()
    console.log('async1 end')}async function async2() {
    console.log('async2')}console.log('script start')
setTimeout(function () {
    console.log('settimeout')
})
async1()
new Promise(function (resolve) {
    console.log('promise1')
    resolve()
}).then(function () {
    console.log('promise2')})console.log('script end')
Copy the code

Analysis process:

  1. Execute the entire code, encounteredconsole.log('script start')Print the result directly and output itscript start
  2. Timer encountered, it is a macro task, put first do not execute
  3. encounterasync1(), the implementation ofasync1Function, print firstasync1 start, the following encounterawaitHow to do? To perform firstasync2To printasync2, then block the following code (join the list of microtasks) and jump out to execute the synchronized code
  4. Jump to thenew PromiseIn this case, just execute, printpromise1, the following encounter.then(), it is a microtask, placed on the microtask list waiting to be executed
  5. The last line is printed directlyscript end, now that the synchronization code is complete, start the microtask, i.eawait The following code, printasync1 end
  6. Move on to the next microtask, executethenThe callback to printpromise2
  7. Now that I’ve done everything for the last macro task, I’m going to do the next macro task, which is timer, printsettimeout

So the final result is: script start, async1 start, Async2, promise1, script end, AsynC1 end, promise2, setTimeout