Why is JS single threaded

The characteristic of a single thread is that only one task can be executed at a time. Because of the interaction with the user and manipulation of the DOM and other related operations, js must use a single thread, otherwise multithreading will cause synchronization problems. If one thread is modifying the DOM and another thread wants to delete the DOM, the shared resource needs to be locked, making the task tedious.

Single thread problem

The single thread will cause the blocking problem, and the next task can be executed only after the previous task is completed. In this way, the page will be stuck and the page will not respond, affecting the user experience. Therefore, the solution of synchronous task and asynchronous task appears.

Synchronous task: a task that is queued on the main thread can be executed only after the first task is completed. Asynchronous task: In the task queue, only when the task queue notifies the main thread that an asynchronous task is ready will it enter the main thread to call.

Specific operation mechanism:

  • All synchronization tasks are executed on the main thread, forming oneExecution stack.
  • There is one outside the main threadTask queueWhenever an asynchronous task has a result, an event is placed in the task queue.
  • When all the synchronous tasks in the execution stack are finished, the event in the task queue will be read, and the asynchronous task corresponding to the event will end the wait state and the execution stack will start.

Event Loop

The main thread continuously reads events from the task queue in a Loop, so the whole operation mechanism is also called an Event Loop.

Macro task

  • The code inside script
  • setTimeout
  • setInterval
  • setImmediate(Node)
  • I/O (ajax request)

Micro tasks

  • Promise(When creating a Promise instance object, the code executes sequentially, if

When the · THEN operation is executed, the task is dispatched to the microtask queue.

  • Process.nexttick (will be executed before promise)
  • MutationObserver

The operation of the loop mechanism

    1. Script content is a macro task that starts with a script tag and is placed on the execution stack when a function call occurs.
  • 2. If you encounter a macro task during this process, put it in a macro task queue.
  • 3. If you encounter a microtask, place the microtask in the microtask queue of the current macro task (each macro task has a microtask queue).
  • 4. After the current macro task is executed, all the callback functions in the microtask queue are executed before exiting the execution stack. (If a new microtask is encountered, the system continues to join the end of the current microtask queue.) After the execution, clear the microtask queue and unstack the microtask.
  • 5. Perform requestAnimationFrame callback, event handling, and page refresh.
  • 6. Fetch the next macro task from the macro task queue and start executing (back to 1).

In short: Perform one macro task, perform a team of microtasks

Test 1

	console.log('1');
	setTimeout(() = > {
		console.log('2')},1000);
	new Promise((resolve, reject) = > {
		console.log('3');
		resolve();
		console.log('4');
	}).then(() = > {
		console.log('5');
	});
	console.log('6');// 1,3,4,6,5,2
Copy the code

Test 2

console.log('1');

setTimeout(function() {
    console.log('2');
    new Promise(function(resolve) {
        console.log('3');
        resolve();
    }).then(function() {
        console.log('4')})setTimeout(function() {
	    console.log('5');
	    new Promise(function(resolve) {
	        console.log('6');
	        resolve();
	    }).then(function() {
	        console.log('7')})})console.log('14');
})

new Promise(function(resolve) {
    console.log('8');
    resolve();
}).then(function() {
    console.log('9')})setTimeout(function() {
    console.log('10');
    new Promise(function(resolve) {
        console.log('11');
        resolve();
    }).then(function() {
        console.log('12')})})console.log('13')
/ / 1,8,13,9,2,3,14,4,10,11,12,5,6,7

https://juejin.cn/post/6913368493035356167
Copy the code

Nodejs the eventloop

Juejin. Cn/post / 691336…

Hejialianghe. Making. IO/jsadvanced /…

How do you implement asynchronous programming

The callback function

Asynchronous tasks must specify callback functions, which are not called directly but are executed when a specific event or condition occurs and are used to respond to that event. AJAX’s onload event, for example, is followed by a callback function, but the constant nesting of callback functions creates callback hell.

ajax("./test1.json".function(data){
	console.log(data);
    ajax("./test2.json".function(data){
    	console.log(data); . })})Copy the code

Callback hell shortcomings

  • Nested functions have coupling properties and will pull the whole body once they are changed.
  • Callbacks cannot use try catch to catch errors. (Because asynchronous tasks are executed when synchronous tasks in the execution stack have already exited, and functions to catch exceptions have also been introduced)
  • Callbacks cannot return directly. (Only callback functions can be terminated, not external code.)

Event publish/subscribe

const pbb = new PubSub();

ajax("./test1.json".function(data){
	Publish test1Success and the requested data on the first ajax request success
	pbb.publish("test1Success",data);
})

// Subscribing to test1Success will be triggered
pbb.subscribe("test1Success".function(data){
	console.log(data);
    // Make a second Ajax request
    ajax("./test2.json".function(data){
    // The second Ajax request successfully published the message
    	pbb.publish("test2Success",data); })})/ / subscribe test2Success
pbb.subscribe("test2Success".function(data){
	console.log(data); . })Copy the code

The realization of the PubSub

class PubSub{
    constructor(){
        // Event queue
        this.events = {}; 
    }
    // Publish events
    publish(eventName, data){
        if(this.events[eventName]){
            this.events[eventName].forEach(cb= > {
                cb.apply(this, data);// Once the event is successfully published, the callback function in the corresponding queue is executed}); }}// Subscribe to events
    subscribe(eventName, callback){
        if(this.events[eventName]){
            this.events[eventName].push(callback);
        }else{
            this.events[eventName] = [callback]; }}// Unsubscribe
    unSubcribe(eventName, callback){
        if(this.events[eventName]){
            this.events[eventName] = this.events[eventName].filter(
                cb= >cb ! == callback ); }}}Copy the code

Encapsulation in NodeJS (Event)

let events = require("events");
const pbb  = new events.EventEmitter();// Create a publish subscription object

ajax("./test1.json".function(data){
	Publish test1Success and the requested data on the first ajax request success
	pbb.emit("test1Success",data);
})

// Subscribing to test1Success will be triggered
pbb.on("test1Success".function(data){
	console.log(data);
    // Make a second Ajax request
    ajax("./test2.json".function(data){
    // The second Ajax request successfully published the message
    	pbb.emit("test2Success",data); })})/ / subscribe test2Success
pbb.on("test2Success".function(data){
	console.log(data);
});
Copy the code

Generator

Juejin. Cn/post / 684490…

State change when using:

  • The generator function is called first, and the internal code is suspended instead of being executed immediately.
  • Calling next() wakes up the generator, performs its current yield, returns an object, and continues to suspend.
  • Until there is no executable code, it returns a result object {value: undefined, done: true}.
function request(url) {
  makeAjaxCall(url, function(response) {
    it.next(response);// The second call returns the result})}function *foo() {
  var data = yield request('http://api.example.com');
  console.log(JSON.parse(data));
}

var it = foo();
it.next();// The first call executes request
Copy the code

Promise

Promise can solve callback hell (promise chain calls) and handle parallel tasks (promise.all).

PromiseA + specification

The use of the promise

Promise is a constructor that takes as an argument a function (which the executor function executes immediately) that takes resolve,reject (also a function).

The attribute of promise

After calling new, p is a Promise instance object, and each instance object has two properties: state and result

There are three possible states

  • Pending (prepare)
  • This is a big pity.
  • Rejected (= rejected)

Perform resolve(STR) to make the state pending-> depressing, and the current promise value is STR

Reject (STR) makes the state pending-> Rejected, and the current promise value is STR

And the state change is one-off (it can only be changed once)

let p = new Promise((resolve,reject) = >{
	resolve("Successful outcome");
    //reject(" reject ");
})
console.log(p);
Copy the code

Promise. Prototype. Then method

The then method has two parameters, both of which are functions. The first function is executed when the state is fulfilled (and the parameter is the promise value), and the second method is executed when the state is Rejected (ibid.).

let p = new Promise((resolve,reject) = >{
	resolve("Successful outcome");
    //reject(" reject ");
})

let ret = p.then((value) = > {
	console.log("Called on success"+ value);
},(err) = > {
	console.log("Call on failure"+ err);
})

Copy the code

The then method returns a new Promise, and the current state is pending. The final return value of then is determined by the result of the execution of the callback function in THEN.

  • If an exception is thrown, the new promise state is Rejected, and Reason is the exception.
  • This is a pity if an arbitrary value is returned that is not a promise, the state of the new promise will be fulfilled, and the value will be the return value
  • If you return a promise, that’s the promise

Because then returns a promise, he can also call THEN, which makes chained calls (solving callback hell/cascading execution tasks) possible.

	new Promise((resolve,reject) = >{
    
    }).then((value) = >{
    	console.log("The first promise succeeds.")
        //return 123; // After returning, the state of the promise will be fulfilled.
        console.log(a);// But if this code fails, it will change the state of the current Promise to Rejected
    },(reason) = >{
    	console.log("The first promise fails.")
    }).then((value) = >{
    	console.log("The second promise succeeds.")},(reason) = >{
    	console.log("The second Promises fail"+reason);// Can capture the cause of the failure
    })
Copy the code

Then is not executed when the state is pending.

This is called when a promise has more than one THEN.

Promise. Prototype. Catch method

Catch, catch, catch A catch catches an error in all three cases (Reject, error, or throw).

	const p = new Promise((resolve,reject) = > {
    //reject();
    //console.log(a);
    throw new Error('Wrong')})let t = p.catch((reason) = >{
    	console.log('failure'+reason);
    })
Copy the code

The most common way to write promise

new Promise((resolve,reject) = >{

}).then((value) = >{
	console.log('success'+value);
}).catch((reason) = >{
	console.log('failure'+reason);
})
Copy the code

Method of use

 function fn(){
     return new Promise((resolve, reject) = >Resolve (data), reject(error)})} fn(). Then (success1, fail1). Then (Success2, fail2)Copy the code

Promise.all/Promise.race

// Wrap multiple Promises into a Single Promise instance object, and only call Success1 if they all succeed
Promise.all([promise1, promise2]).then(success1, fail1)

const p1 = Promise.resolve(1);// Return a Promise object with a success value of 1
const p2 = Promise.reject(2);
const p3 = Promise.resolve(3);

const pAll = Pomise.all([p1,p2,p3]);
pAll.then(
	(value) = > {
    	console.log('success'+value)/ / [1, 3]
    },
    (reason) = > {
     	console.log('failure'+reason)/ / 2})Copy the code
// Then is called by the first state-determined promise in the array
 Promise.race([promise1, promise2]).then(success1, fail1)
Copy the code

Write a Promise

Promise constructor, then, catch

//1. Define structure without writing concrete implementation
//ES5 custom module
(function(window){
    // The constructor passes an executor function
    function Promise(executor){
        let _this = this;
        _this.status = 'pending';// Store the current state
        _this.data = undefined;// Store the current value
        _this.callbacks = [];// Each object has two callback functions onResolved onRejected

        function resolve(value){
            if(_this.status ! = ='pending') return;// State can only be changed from pending
            _this.status = 'fulfilled';/ / state
            _this.data = value;/ / value
            if(_this.callbacks.length){// Execute each onResolved asynchronously and pass the vaule value if there is already a callback
                setTimeout(() = >{
                    _this.callbacks.forEach((callbackObj) = >{ callbackObj.onResolved(value); })},0); }}function reject(reason){
            if(_this.status ! = ='pending') return;
            _this.status = 'rejected';
            _this.data = reason;
            if(_this.callbacks.length){
                setTimeout(() = >{
                    _this.callbacks.forEach((callbackObj) = >{ callbackObj.onRejected(reason); })},0); }}try{
            executor(resolve,reject);Execute executor immediately.
        }catch(err){
            reject(err);// Call rejecte if an error occurs}}Promise.prototype.then = function(onResolved, onRejected){
        let _this = this;// Save the Promise object that calls Then

        onResolved = typeof onRejected === 'function'? onRejected : value= > value;// Pass a function if it is not a callback
        onRejected = typeof onRejected === 'function'? onRejected : reason= > {throw reason};// The transmission is abnormal

        return new Promise((resolve, reject) = >{

            // Executes the specified callback and changes the state that returns promise
            function handle(callback){
                try{
                    const result =  callback(self.data);
                    if(result instanceof Promise) {//3.返回的是一个promise, 返回promise就是这个promise
                        result.then(resolve,reject);// If result succeeds, resolve, which returns a promise, is called
                    }else{
                        resolve(result);//2. The return is not a Promise, and the return Promise is a pity}}catch(err){
                    reject(err);//1. Throw an exception and return the promise (Rejected)}}// Determine the status of the caller
            if(_this.status === 'rejected') {// Asynchronously execute onRejected and pass the parameter if the caller fails
                setTimeout(() = >{ handle(onRejected); })}else if(_this.status === 'fulfilled') {// If the caller succeeds, execute onResolved and pass the parameter
                setTimeout(() = >{ handle(onResolved); })}else{// The caller state is Pending and saves onResolved and onRejected
                self.callbacks.push({
                    onResolved(value){
                        handle(onResolved);
                    },
                    onRejected(reason){ handle(onRejected); }})}})}Promise.prototype.catch = function(onRejected){
        return this.then(undefined,onRejected);
    }

    // Return a successful Promise
    Promise.resolve = function(value){
        return new Promise((resolve,reject) = >{
            if(value instanceof Promise) {// A promise is passed in
                value.then(resolve,reject);
            }else{// An ordinary value is passed inresolve(value); }})}// Return a failed Promise
    Promise.reject = function(reason){
        return new Promise((resolve,reject) = >{ reject(reason); })}Promise.all = function(promises){
        const values = [];// Define the return value of the array saved successfully
        let count = 0;
        return new Promise((resolve, reject) = >{// Return a new promise
            // Iterate over the results of each promise
            promises.forEach((p, index) = >{
                if(! pinstanceof Promise) {// If there is a non-promise object in the array, consider it a success
                    values[index] = value;
                    count++;
                }else{
                    p.then(
                        value= >{
                            values[index] = value;// If p is successful, save it in the same order as Promises
                            count++;
                            if(count === promises.length){
                                resolve(values);// Return promise as success and pass in the array}},reason= >{// If one fails, return a promisereject(reason); })})})}// Returns the first completed/failed result
    Promise.race = function(promises){
        return new Promise((resolve, reject) = >{
            promises.forEach((p) = >{
                p.then((value) = >{
                    resolve(value);
                },(reason) = >{ reject(reason); })})})}window.Promise = Promise;// Mount it to window}) ()// Execute the function immediately anonymously
Copy the code
<! DOCTYPEhtml>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <title>Document</title>
</head>
<body>
    <script src="./newPromise.js"></script>
    <script>
        new Promise((resolve, reject) = > {

            setTimeout(() = >{
                console.log("Delay completed for 1s")
                resolve(1);
            },1000);

            console.log("State change successful")
        }).then((value) = >{
            console.log('onResolved1()'+value);
            return new Promise((resolve,reject) = >{
                setTimeout(() = >{
                    resolve(2);
                    console.log('Finish two seconds late')},2000)})},(reason) = >{
            console.log("onRejected()" + reason);
        }).then((value) = >{
            console.log('onResolved2()'+ value);
            throw 3;
        },(reason) = >{
            console.log("onRejected2()" + reason);
        }).catch((reason) = >{
            console.log("onRejected3()" + reason);
        })
        /* Status change succeeded test.html:15 delay for 1s to complete test.html:21 onResolved1()1 test.html:25 Delay for 2 seconds to complete test.html:32 onResolved2()2 test.html:37 onRejected3()3 */
    </script>
</body>
</html>
Copy the code

// ES6 writing style
(function(Window){
    class Promise{
        constructor(exector){}function reject(reason){}// Defined on the prototype
        then(onResolved, onRejected){}catch(onRejected){
           
        }
    
        // Defined on class objects
        // Return a successful Promise
        static resolve = function(value){}// Return a failed Promise
        static reject = function(reason){}static all = function(promises){}// Returns the first completed/failed result
        static race = function(promises){
    }


    Window.Promise = Promise;// Mount it to window}) ()// Execute the function immediately anonymously

Copy the code

test

setTimeout(() = >{
    console.log("0");
})
// 1 2 3 3 4 6 5 0
new Promise((resolve, reject) = >{
    console.log("1");
    resolve();
}).then(() = >{
    console.log("2");
    new Promise((resolve, reject) = >{
        console.log("3");
        resolve();
    }).then(() = >{
        console.log("4");
    }).then(() = >{
        console.log("5");
    })
}).then(() = >{
    console.log("6");
})

new Promise((resolve, reject) = >{
    console.log("Seven");
    resolve();
}).then(() = >{
    console.log("8");
})
Copy the code

async await

The async and await keywords provide a more concise way to write asynchronous behavior based on promises without intentionally chain-calling promises.

The return value of async is a Promise object, and the expression to the right of await is usually a Promise, but may not be.

Await will return the fulfilled value, so you need to use try{}catch(){} to get the fulfilled value.

        function fn1(){
            return new Promise((resolve,reject) = >{
                setTimeout(() = >{
                    resolve(1);
                },1000)})}async function fn2(){
            let result = await fn1();
            console.log(result);
            return new Promise((resolve,reject) = >{
                setTimeout(() = >{
                    reject(2);
                },1000)})}async function fn3(){
            try{
                let result = await fn2();
                console.log(result);
            }catch(err){
                console.log(err);
            }
        }

        fn3();
Copy 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");
}, 0);
async1();
new Promise(function (resolve) {
    console.log("promise1");
    resolve();
}).then(function () {
    console.log("promise2");
});
    console.log("script end");
/*
script start
async1 start
async2
promise1
script end
async1 end
promise2
settimeout
*/
Copy the code

AJAX

Juejin. Cn/post / 684490…

AJAX is asynchronous javascript and XML that can exchange data with the server and update parts of the web without reloading the page.

Four steps

  • Create an Ajax instance object using new XMLHttpRequest.
  • Use the open method to set the address and mode of request.
  • Use send() to send data
  • ResponseText gets the data returned by the server

A get request

let btn = document.getElementById('btn');
let username = document.getElementById('username');
let age = document.getElementById('age');

btn.onlick = function(){
    let xhr = new XMLHttpRequest();
    let name = username.value;
    let age = username.value;
    let params = 'username='+name+'&age='+age;
    xhr.open('get'.'https://localhost:3000/get? '+params);
    xhr.send();
    xhr.onload = function(){
	 console.log(xhr.responseText); }}Copy the code

Post request (request format: Attribute name = attribute value)

let btn = document.getElementById('btn');
let username = document.getElementById('username');
let age = document.getElementById('age');

btn.onlick = function(){
    let xhr = new XMLHttpRequest();
    let name = username.value;
    let age = username.value;
    let params = 'username='+name+'&age='+age;
    xhr.open('post'.'https://localhost:3000/post');
    xhr.setRequestHeader('Content-Type'.'application/x-www-form-urlencoded');
    xhr.send(params);
    xhr.onload = function(){
	 console.log(xhr.responseText); }}Copy the code

Post request (request format is JSON)

let xhr = new XMLHttpRequest();
xhr.open('post'.'http://localhost:3000/json');
xhr.setRequestHeader('Content-Type'.'application/json');
xhr.send(JSON.stringify({name:'tom'.age:19}));
xhr.onload = function(){
	console.log(xhr.responseText);
}
Copy the code

Ajax status code

  • 0 is initialized and Open is not called
  • 1 Start. Open is called, but send is not called
  • 2 Send. The message has been sent, but no response has been received
  • 3 Receive: Some data has been received
  • 4 Completed, and fully received data can be used in the client.
let xhr = new XMLHttpRequest();
xhr.open('get'.'http://localhost:3000/readystate');
console.log(xhr.readyState);/ / 1
xhr.onreadystatechange = function(){
	console.log(xhr.readyState);
    if(xhr.readyState === 4) {console.log(xhr.responseText);
}
}
xhr.send();
Copy the code

Encapsulation AJAX

<script type="text/javascript">
	function ajax (options) {
		// Stores default values
		var defaults = {
			type: 'get'.url: ' '.data: {},
			header: {
				'Content-Type': 'application/x-www-form-urlencoded'
				},
			success: function () {},
			error: function () {}};// Override properties in the defaults object with properties in the Options object
		Object.assign(defaults, options);
		// Create ajax objects
		var xhr = new XMLHttpRequest();
		// Concatenate the variables of the request parameters
		var params = ' ';
		// Loop over the object format parameters passed in by the user
		for (var attr in defaults.data) {
				// Convert arguments to string format
				params += attr + '=' + defaults.data[attr] + '&';
			}
		// Truncate the & at the end of the argument
		// Reassign the truncated result to the params variable
		params = params.substr(0, params.length - 1);
		// Determine the request type
		if (defaults.type == 'get') {
				defaults.url = defaults.url + '? ' + params;
			}
		// Configure the Ajax object
		xhr.open(defaults.type, defaults.url);
		// If the request is POST
		if (defaults.type == 'post') {
				// The type of request parameters that the user wants to pass to the server
				var contentType = defaults.header['Content-Type']
				// Sets the type of request parameter format
				xhr.setRequestHeader('Content-Type', contentType);
				// Determine the type of request parameter format the user wants
				// If type is json
				if (contentType == 'application/json') {
					// Pass json data format parameters to the server
					xhr.send(JSON.stringify(defaults.data))
				}else {
					// Pass the normal type of request parameters to the serverxhr.send(params); }}else {
			// Send the request
			xhr.send();
		}
		// Listen for the onload event under the XHR object
		Emitted when the XHR object has received the response data
		xhr.onload = function () {
			// xhr.getResponseHeader()
			// Get the data in the response header
			var contentType = xhr.getResponseHeader('Content-Type');
			// Data returned by the server
			var responseText = xhr.responseText;
			// If the response type contains applicaition/json
			if (contentType.includes('application/json')) {
				// Convert json strings to JSON objects
				responseText = JSON.parse(responseText)
			}
			// When the HTTP status code is 200
			if (xhr.status == 200) {
				// The request was successfully invoked to handle the success of the function
				defaults.success(responseText, xhr);
			}else {
				// Request failure calls the function that handles the failure case
				defaults.error(responseText, xhr);
			}
		}
	}
	ajax({
		type: 'post'.// Request an address
		url: 'http://localhost:3000/responseData'.success: function (data) {
			console.log('Here is the success function');
			console.log(data)
		}
	})
</script>
Copy the code

Fetch (modern browsers come with it, IE doesn’t)

        fetch("http://localhost:91/", {method:"POST".headers: {"content-type":"application/x-www-form-urlencoded"},
            body:"a=12&b=77"
        }).then(
            res= > res.json()
        ).then(
            data= > {console.log(data)}
        )
Copy the code

Axios (default JSON format)

axios({
	url:'http://localhost:90? b=12'.method:'POST'.data: {a:1.b:12}
}).then(res= >{console.log(res.data)})
Copy the code