JS asynchronous programming and evolution history

1. What is asynchronous programming?

First of all, we need to understand that JS is a single-threaded language. In order to avoid thread synchronization problems. One person performs a task. If there are multiple tasks, the tasks need to be queued and executed one by one.

The advantage of single threading is that it is safer and simpler, but the disadvantages are also obvious. If a task in the middle is particularly time-consuming, then there will be blocking. So in order to solve this problem, JS has two modes of performing tasks:

Synchronous mode (Synchronous) and asynchronous mode (Asynchronous).

Synchronous mode: The code is executed in sequence, and the next task must wait for the end of the previous task before it can be executed.

console.log('global begin')
function bar () {
    console.log('bar task') 
}
function foo () {
    console.log('foo task')
    bar()
}
foo()
console.log('global end')

// global begin
// foo task
// bar task
//global end

Asynchronous mode: It does not wait for the end of this task to start the next task, and executes the next task immediately after it is turned on. The subsequent logic of the time-consuming function will be defined in the form of a callback function. Internally, the incoming callback function is automatically executed after the time-consuming task is completed.

console.log('global begin')
// 延时器
setTimeout(function timer1 () {
    console.log('timer1 invoke')
}, 1800)
// 延时器中又嵌套了一个延时器
setTimeout(function timer2 () {
    console.log('timer2 invoke')
    setTimeout(function inner () {
        console.log('inner invoke')
    }, 1000)
}, 1000)
console.log('global end')

// global begin
// global end
// timer2 invoke
// timer1 invoke
// inner invoke

The difficulty of asynchronous patterning is the order of execution. // Here you should understand the call stack, message queue and event loop

Two, understand Promse

Callback function is the foundation of asynchronous programming, but there will be a problem of callback hell. In order to avoid this problem, CommonJs proposed the Promise specification, which is called the language specification in Es6.

Promise is an object used to express whether an asynchronous task succeeds or fails after execution.

// promise基本用法

const promise = new Promise((resolve,reject)=>{
    if (true) {
        resolve(100)  // 执行成功返回resolve
    } eles {
         reject(new Error('promise rejected')) // 执行失败返回reject
    }
})

promise.then((value)=>{
    console.log('resolved',value) // resolve 100
},(error)=>{
    console.log('reject',error) // rejected Error: promise rejected
})

 -Even if there is no asynchronous operation in the promise, the callback function of the then method will still enter the event queue.

-Essence of Promise: In essence, it also uses a callback function to define the tasks that need to be executed after the asynchronous task ends. The callback function here is passed through the then method

-Promise chain call

1. Common Misunderstandings-The most common misunderstanding of using Promises is the way of nesting. The chain call method of promises should be used to ensure the flatness of asynchronous tasks as much as possible.

2. The understanding of chain call-the then method of the promise object returns a brand new promise object. You can continue to call the then method. If the return is not a promise object, but a value, then this value will be passed as the value of the resolve. If there is no value, the default is undefined-the following then method is the Promise returned for the previous then Register callback-the return value of the callback function in the previous then method will be used as the parameter of the callback of the then method-if the callback returns a Promise, then the callback of the then method will wait for its end

-Promise exception handling 

1. The onRejected method of callback in then

2. .catch() (recommended) If there is an exception in the promise, the reject method will be called. You can also use .catch(). It is more common to use the .catch method because it is more in line with chain calls. .catch is a failed callback registered for the entire promise chain

// 使用Promise去封装一个ajax的案例
function ajax (url) {
  return new Promise((resolve, rejects) => {
    // 创建一个XMLHttpRequest对象去发送一个请求
    const xhr = new XMLHttpRequest()
    // 先设置一下xhr对象的请求方式是GET,请求的地址就是参数传递的url
    xhr.open('GET', url)
    // 设置返回的类型是json,是HTML5的新特性
    // 我们在请求之后拿到的是json对象,而不是字符串
    xhr.responseType = 'json'
    // html5中提供的新事件,请求完成之后(readyState为4)才会执行
    xhr.onload = () => {
      if(this.status === 200) {
        // 请求成功将请求结果返回
        resolve(this.response)
      } else {
        // 请求失败,创建一个错误对象,返回错误文本
        rejects(new Error(this.statusText))
      }
    }
    // 开始执行异步请求
    xhr.send()
  })
}

ajax('/api/user.json').then((res) => {
  console.log(res)
}, (error) => {
  console.log(error)
})

- Promise.all

   You can wrap multiple Promise instances into a new Promise instance. At the same time, the return values ​​of success and failure are different. When successful, it returns an array of results, and when it fails, it returns the value of the first rejected failure status.

let wake = (time) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(`${time / 1000}秒后醒来`)
    }, time)
  })
}

let p1 = wake(3000)
let p2 = wake(2000)

Promise.all([p1, p2]).then((result) => {
  console.log(result)       // [ '3秒后醒来', '2秒后醒来' ]
}).catch((error) => {
  console.log(error)
})

// Promise.all获得的成功结果的数组里面的数据顺序和接收到的数组顺序是一致的,即p1的结果在前,即便p1的结果获取的比p2要晚。

- Promise.race

  Promse.race means race, which means that if the result in Promise.race([p1, p2, p3]) is obtained quickly, that result will be returned, regardless of whether the result itself is a success state or a failure state.

let p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('success')
  },1000)
})

let p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('failed')
  }, 500)
})

Promise.race([p1, p2]).then((result) => {
  console.log(result)
}).catch((error) => {
  console.log(error)  // 打开的是 'failed'
})

- Promise execution timing / macro task and micro task

console.log(global start);
setTimeout(()=>{
    console.log('setTimeout')
},0)
Promise.resolve()
.then(()=>{
    console.log('promise')
})
.then(()=>{
    console.log('promise2')
})
console.log('global end')

// global start
// global end
// promise
// promise1
// setTimeout

// 回调函数中的任务称为宏任务
// 微任务:提高整体的响应能力 promise&process.nectTick&MutationObserver

Macro task and micro task operation mechanism:

  • Execute a macro task (get it from the event queue if it is not in the stack)
  • If you encounter a micro task during execution, add it to the task queue of the micro task
  • After the macro task is executed, immediately execute all micro tasks in the current micro task queue (execute in sequence)
  • After the current macro task is executed, the rendering is checked, and then the GUI thread takes over the rendering
  • After rendering, the JS thread continues to take over and starts the next macro task (obtained from the event queue)

legend:

Three, understand Generator

A generator is a new data type introduced by the ES6 standard. A generator looks like a function, but it can return multiple times.

function* foo(x) {
    console.log('start')
    try {
        const res = yield 'foo';
        console.log(res); // bar
        const urls = yield ajax('...');
    } catch(e) {
        console.log(e)
    }
}

// yield 返回关键词

const generator = foo();
const result = generator.next('bar'); // 传入的参数在yield 前可以接收

generator.throw(new Error('Generator error')) // 抛出异常的函数

/** 
next()方法会执行generator的代码,然后,每次遇到yield x;就返回一个对象{value: x, done: true/false},然后“暂停”。返回的value就是yield的返回值,done表示这个generator是否已经执行结束了。如果done为true,则value就是return的返回值。

当执行到done为true时,这个generator对象就已经全部执行完毕,不要再继续调用next()了。
**/


// generator异步方案 -- 递归执行异步调用
function handleResult(result) {
    if(result.done) return
    result.value.then(data => {
        handleResult(generator.next(data))
    },error => {
        generator.throw(error)
    })
}

// 了解co异步方案

Four, Async / Await Syntactic Sugar-Asynchronous Programming Standard at the Language Level

async function foo(x) {
    console.log('start')
    try {
        const res = await ajax('...url11');
        const urls = await ajax('...url2');
    } catch(e) {
        console.log(e)
    }
}

// await 只能出现在async里面
// await后面接一个会return new promise的函数并执行它
const promise = main();
promise.then(()=>{
    console.log('all complete')
})

Reference link: 

https://blog.csdn.net/wu_xianqiang/article/details/105837869  (Thorough understanding that JS is single-threaded)

https://www.cnblogs.com/lelexiu/p/10095718.html  (Understanding the call stack, event loop, message queue)

Guess you like

Origin blog.csdn.net/qq_42269433/article/details/114939867