2

WechatIMG20.jpeg

1. Reasons for Asynchrony

js is single-threaded and can only do one thing at a time, so all things are queued, waiting to be executed, but if one of the things takes a long time in the queue, then the next event will be Been waiting. For example, if we open a webpage and the interface request time is 5 seconds, then after the page waits 5 seconds, and then renders the page, the page will remain blank for a long time, which will affect the user experience. Therefore, js designed an asynchronous mode under the flaws of the synchronization mechanism

for example

  • 1. Whether you cut the vegetables first or cook the rice in the rice cooker first, you have to wait for one thing to complete before proceeding to the next one. This is an example of synchronization
  • 2. When cutting vegetables, you can also use a rice cooker to cook rice (no need to wait for you to finish cutting). This is an asynchronous example. Let's use code to demonstrate asynchronous
function a() {
  console.log("a");
  setTimeout(() => {
    console.log("a1");
  }, 1000);
}
function b() {
  console.log("b");
}
a();
b();
//打印结果 a,b,a1
//这里b不用等待一秒后a1打印完再打印,这里可以结合js的事件循环机制一起看,更加细节
// 附上js的事件循环机制文章:https://segmentfault.com/a/1190000039866826

2. Asynchronous solution

1. Callback function

The concept of a callback function: A function that is passed into another function as an actual parameter and is called in the external function to complete certain tasks is called a callback function.

function b(value) {
  var bb = "everyone";
  console.log(value + bb);
}
function a(callback) {
  let value = "hello ";
  setTimeout(() => {
    callback(value);
  }, 1000);
}
a(b);
//这是一个异步回调,1秒钟之后才会执行b函数

Disadvantages of callback function: easy to write callback hell (Callback hell)

  • Nested functions are coupled, once they are changed, they will affect the whole body.
  • With too many nested functions, it is difficult to handle errors (you cannot use try catch, and you cannot return directly)
let a = setTimeout(function a() {
  var name1 = "hello ";
  try {
    setTimeout(function b() {
      var name2 = "everyone " + name1; //如果这里的b函数,name2改动了,下面的c函数打印的值就会受影响,牵一发而动全身
      setTimeout(function c() {
        var name3 = "yeah!";
        return name3; // 在这里return只是为了演示,return name3 不能被接收到
        console.log(name2 + name3);
      }, 1000);
    }, 1000);
  } catch (e) {
    console.log(e, "不能捕获错误");  //这个try,catch只是为了演示,这里不能捕获到错误,因为try catch不能捕获异步错误,当执行到try时,try里面的代码放到到异步的任务队列里,没有try到任何内容,所以catch里面不打印。让同步代码执行完,去执行异步的任务队列时,这时候会报错。
  }
}, 1000);
console.log(a); //这里a不是name3,所以不能直接return

It is precisely because of the flaws in the callback function that promises were born in es2015

2.Promise

The concept of Promise: Promise object is used to represent the final completion (or failure) of an asynchronous operation and its result value

Handwritten Promise (will be expanded around the method in the figure below)

WechatIMG371.jpeg

Use of Promise

WechatIMG368.jpeg

Promise is always pending. Only when the resolve or reject method is called, the state will change. At this time, the corresponding callback function will be executed according to the status of success or failure.
new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("成功");
    // reject("失败");
  }, 1000);
}).then(
  (value) => {
    console.log(value, "这是成功返回的值");
  },
  (reason) => {
    console.log(reason, "这是失败的原因");
  }
);

The following is the implementation of the core logic of the handwritten Promise class

  • A Promise is a class (or a constructor, only classes are mentioned here). When executing this class, you need to pass an executor into it, and the executor will execute immediately
  • There are two parameters in the executor, one is resolve (success) and the other is reject (failure)
  • Since resolve and reject are executable, they are both functions

    class Promise {//类
    constructor(executor) { // 构造函数
      // 成功
      let resolve = () => { };
      // 失败
      let reject = () => { };
      //执行executor可能会报错,把错误捕获,传递到reject里面去
      try {
        executor(resolve, reject);
      } catch (err) {
        reject(err);
      }
    }
    }
  • Promise has three states: Fulfilled, Rejected, and Pending.

    • Pending--->Fulfilled
    • Pending--->Rejected
    • Once the status is confirmed, it cannot be changed
  • Resolve and reject are used to change the state

    • resolve()--->Fulfilled
    • reject()--->Rejected
const PENDING = "pending"; //等待
const FULFILLED = "fulfilled"; // 成功
const REJECTED = "rejected"; // 失败
class Promise {
  constructor(exector) {
    const resolve = (value) => {
    // 一但状态确定就不可更改
      if (this.status !== PENDING) return;
      //更改状态,状态变成成功
      this.status = FUlFILLED;
       //把成功的值保持下来
      this.value = value;
    };
   const reject = (reason) => {
      if (this.status !== PENDING) return;
      this.status = REJECTED;
      this.reason = reason;
    };
    try {
      exector(resolve, reject); //立即执行,resolve
    } catch (e) {
      reject(e);
    }
  }
  status = PENDING;
  value = undefined;
  reason = undefined;
  fulfilledCallBack = undefined;
  rejectedCallBack = undefined;
}
  • What the then method does internally is to judge the state. If the state is successful, the successful callback function is called; if the state fails, the failed callback function is called.
class Promise{
  constructor(executor){...}
  status = PENDING;
  value = undefined;
  reason = undefined;
  // then 方法 有两个参数onFulfilled onRejected
  then(successCallback, failCallback) {
    // 状态为fulfilled,successCallback,传入成功的值
    if (this.status === FULFILLED) {
    //用try,catch捕获在then方法里面抛出的错误,传入reject中
      try {
        successCallback(this.value);
      } catch (error) {
        reject(error);
      }
    }
    // 状态为rejected,执行failCallback,传入失败的原因
    if (this.status === REJECTED) {
      try {
        failCallback(this.reason);
      } catch (error) {
        this.reject(error);
      }
    }
}
  • Now you can basically implement simple synchronization code, but when resolve is executed in setTomeout, the status is still pending when then, so at this time, successCallback and failCallback should be stored, and once resolve or reject, they will be called
  • Now you can basically implement simple synchronization code, but the same promsie calls then multiple times. If resolve or reject is written in setTomeout and executed, only the last then method will be executed, because failCallback or failCallback is saved as a variable. Should be saved as an array

WechatIMG25.jpeg

const PENDING = "pending"; 
const FULFILLED = "fulfilled"; 
const REJECTED = "rejected"; 
class MyPromise {
constructor(exector) {
    const resolve = (value) => {
    // 一但状态确定就不可更改
      if (this.status !== PENDING) return;
      //更改状态,状态变成成功
      this.status = FUlFILLED;
       //把成功的值保持下来
      this.value = value;
      // if (this.fulfilledCallBack) this.fulfilledCallBack(value);
      if (this.fulfilledCallBack) {
        this.fulfilledCallBack.map((item) => {
          item(value);
          return;
        });
      }
    };
   const reject = (reason) => {
      if (this.status !== PENDING) return;
      this.status = REJECTED;
      this.reason = reason;
      // if (this.rejectedCallBack) this.rejectedCallBack(reason);
      if (this.rejectedCallBack) {
        this.rejectedCallBack.map((item) => {
          item(reason);
          return;
        });
      }
    };
    try {
      exector(resolve, reject); 
    } catch (e) {
      reject(e);
    }
  }
  status = PENDING;
  value = undefined;
  reason = undefined;
  fulfilledCallBack = [];
  rejectedCallBack = [];
  then(successCallback, failCallback) {
    if (this.status === FULFILLED) {
      successCallback(this.value);
    } else if (this.status === REJECTED) {
      failCallback(this.reason); 
    } else {  
      // this.fulfilledCallBack=successCallback;
      // this.rejectedCallBack=failCallback;
       // 这里是pending的状态
      this.fulfilledCallBack.push(successCallback);  
      // 用数组把多个成功或者失败回调存储起来,等到resolve或者reject的时候,依次执行
      this.rejectedCallBack.push(failCallback);
    }
  }
}
  • The parameters of the then method are optional

    • then() is equivalent to ----> then(value=>value,(reason)=>(throw reason))

WechatIMG24.jpeg

const PENDING = "pending"; 
const FULFILLED = "fulfilled"; 
const REJECTED = "rejected"; 
class MyPromise {
  constructor(executor){...}
  status = PENDING;
  value = undefined;
  reason = undefined;
  fulfilledCallBack = [];
  rejectedCallBack = [];
  then(successCallback, failCallback) {
  // 这里是如果then没有传参数,对应的
  // then() 相当于----> then(value=>value,(reason)=>{throw reason})
  const successCallback = successCallback?successCallback:(value)=>value
  const failCallback = failCallback?failCallback:(error)=>{throw error}
  
    if (this.status === FULFILLED) {
      successCallback(this.value);
    } else if (this.status === REJECTED) {
      failCallback(this.reason); 
    } else {  
      this.fulfilledCallBack.push(successCallback);  
      this.rejectedCallBack.push(failCallback);
    }
  }
  • Chain call of then method

    • then() returns a promise object
    • Pass the return value of the previous then method to the next then.
    • There are two types of results returned by then(). One is a promise object. At this time, resolve (value) or reject (reason) in the success or failure callback of the promise.
    • One is the ordinary value of the non-promise object, which directly returns resolve(value)
  • It cannot return itself in the then method, and a "circular reference" error is reported

image.png

const PENDING = "pending"; 
const FULFILLED = "fulfilled"; 
const REJECTED = "rejected"; 
class MyPromise {
  constructor(executor){...}
  status = PENDING;
  value = undefined;
  reason = undefined;
  fulfilledCallBack = [];
  rejectedCallBack = [];
  then(successCallback, failCallback) {
    successCallback = successCallback ? successCallback : (value) => value;
    failCallback = failCallback
      ? failCallback
      : (reason) => {
          throw reason;
        };
    let p = new MyPromise((resolve, reject) => {
      //then是链式调用的,1.所以返回promsie对象
      // 2.把上一个then返回的值传递下去(resolve(value)或者reject(reason)),如果是promise,判断paromsie的状态,成功还是失败,调用resolve或者reject把状态告知下一个then方法
      if (this.status === FULFILLED) {
        setTimeout(() => {
        // 这里的定时器是为了得到p, 因为这里p要在new promise()执行完才得到,现在在执行过程中,没法得到,要使用定时器,变成异步,得到 p
          try {
           // 这里用try catch 是把then()里面的错误捕获,传给reject
            const x = successCallback(this.value);
            getValueType(p, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        });
      } else if (this.status === REJECTED) {
      // 类似成功时候的回调
        setTimeout(() => {      
          try {
            const x = failCallback(this.reason);
            getValueType(p, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        });
      } else {
          this.fulfilledCallBack.push((value) =>
          setTimeout(() => {
            try {
              getValueType(p, successCallback(value), resolve, reject);
            } catch (e) {
              reject(e);
            }
          })
        );
        this.rejectedCallBack.push((reason) =>
          setTimeout(() => {
            try {
              getValueType(p, failCallback(reason), resolve, reject);
            } catch (e) {
              reject(e);
            }
          })
        );
      }
    });
    return p;
  }
}
// 单独抽出一个方法,判断then返回;
// 返回的如果是普通值,直接resolve(value);
// 如果返回的是promise对象,先执行then方法,判断状态,在成功的回调里resolve(value),失败的回调里reject(reason);

const getValueType = (p, x, resolve, reject) => {
// 判断x 和p 是否相等,相等就是自己调用自己,报“循环引用”错误
  if (p === x) {
    return reject(
      new TypeError("Chaining cycle detected for promise #<Promise>")
    );
  }
  // 用instanceof判断x是不是MyPromise的实例对象
  if (x instanceof MyPromise) {
    return x.then(resolve, reject); //简写
  } else {
    return resolve(x);
  }
};
  • catch the error

    • Return a promise error
    • Accept a callback function, catch the error
    ⚠️Note: Here both the failure callback and catch of the chained then can capture errors, but the failure callback of then can only capture the errors of the current promises, and cannot capture the errors in the success callback function of the current then. But catch can catch all errors, so when chaining calls, use catch to catch errors
class MyPromise {
  constructor(executor){...}
  status = PENDING;
  value = undefined;
  reason = undefined;
  fulfilledCallBack = [];
  rejectedCallBack = [];
  then(){...}
  catch(failCallBack) { 
  // 相当于then()第一个参数是undefined
    return this.then(undefined, failCallBack); 
  }
}
const getValueType =()=>{...}
  • Promise.all(), Promise.race method

    • Promise.all() allows us to get the results of asynchronous code execution in the order of asynchronous code calls
    • Promise.race() asynchronous code calls which result is obtained quickly, and that result is returned, regardless of whether the result itself is a success state or a failure state
    class MyPromise {
    constructor(executor){...}
    status = PENDING;
    value = undefined;
    reason = undefined;
    fulfilledCallBack = [];
    rejectedCallBack = [];
    then(){...}
    catch() {...}
    static all(array) {
      let result = [];
      let key = 0;
      return new MyPromise((resolve, reject) => {
        function addData(i, value) {   
          result[i] = value;
          key++;
          // 判断key === array.length,这时候,所以的异步promise才执行完毕
          if (key === array.length) {   
          // 之所以不在for下面执行resolve,在这里执行,因为如果数组里面是异步的promise对象的话,要等它执行完在返回再resolv出去
            resolve(result); 
          }
        }
        for (let i = 0; i < array.length; i++) {
          if (array[i] instanceof MyPromise) { 
          //是promise对象就把成功的值push进数组,失败的值reject出去
            array[i].then(
              (value) => addData(i, value),
              (reason) => reject(reason)
            );
          } else {
            addData(i, array[i]); //是普通值就push进数组
          }
        }
        //   resolve(result); // 如果在这里执行resolve的话,不会等promise对象执行完毕
      });
    }
    static race(array) {
      return new MyPromise((resolve, reject) => {
        for (let i = 0; i < array.length; i++) {
          if (array[i] instanceof MyPromise) {
          // 成功返回第一个resolve,失败返回第一个reject
            array[i].then(resolve, reject); 
          } else {
          // 会返回第一个resolve,因为状态已经改变了,后面resolve不会再执行
            resolve(array[i]); 
          }
        }
      });
    }
    }
    const getValueType =()=>{...}
    
    
  • Promise.resolve(), Promise.reject() methods

    • If the incoming promise object, return it intact
    • If the incoming value is a normal value, wrap it into a promise object and return
    class MyPromise {
    constructor(executor){...}
    status = PENDING;
    value = undefined;
    reason = undefined;
    fulfilledCallBack = [];
    rejectedCallBack = [];
    then(){...}
    catch(){...}
    static resolve(value) {
      if (value instanceof MyPromise) return value;
      return new MyPromise((resolve, reject) => {
        resolve(value);
      });
    }
    static reject(value) {
      if (value instanceof MyPromise) return value;
      return new MyPromise((resolve, reject) => {
        reject(value);
      });
    }
    }
    const getValueType =()=>{...}
  • finally will be executed regardless of failure or success

    • Finally, whether the result is success or failure, it will be executed once
    • Finally, you can call the then method in a chain to get the final result returned by the current promise
    • ⚠️Note: here the finally callback function may return a promsie object, then the subsequent then() has to be executed after the finally is executed, here we need to use the resolve method
class MyPromise {
  constructor(executor){...}
  status = PENDING;
  value = undefined;
  reason = undefined;
  fulfilledCallBack = [];
  rejectedCallBack = [];
  then(){...}
  catch(){...}
  static resolve(value){...}
  finally(callBack) {
    // 1.首先要知道状态,那调用then()就能知道状态
    return this.then(
      // 2.return出去,是因为要返回一个promsie去链式调用
      (value) => {
        // callBack();
        // return value; // 3.还有把值return出去,便于下一个then()接收
        return MyPromise.resolve(callBack()).then(() => value); // 这里是finally的回调函数可能返回一个promsie对象,那就要等这个promsie对象执行完毕,再执行后面的then方法,于是,不管是返回普通值还是promsie对象,直接调用resolve()转成promise对象
      },
      (reason) => {
        // callBack();
        // throw reason;
        return MyPromise.resolve(callBack()).then(() => {
          throw reason;
        });
      }
    );
  }
}
const getValueType =()=>{...}

So far, the basic function of promise has been roughly realized. In fact, promise.then is similar to the idea of a callback function, except that it distinguishes between the success and failure callback functions, and the chain call of the then method solves the nesting hell of the callback function and flattens it. However, the readability of our traditional synchronization code was still not achieved, so Generator appeared.

3.Generator

  • The generator function has two characteristics, one (usually written after function, function ), and one yield.
  • The generator function returns a generator object, and the body of this function will not be executed until we call it through .next.
  • You can use the feature of yield to pause the generator function. Use generator functions to achieve a better asynchronous experience.
function* fun1() {
  console.log("start");
  try{
      // 在函数内部,可以随时通过yield向外返回一个值
      // yield关键词不会向return一样结束这个生成器函数的执行,只是暂停这个生成器函数的执行。直到外界再去执行yield方法时,从yield这个位置继续往下执行
      let aa = yield "foo";   
      // 这里"bar",会作为yield "foo"的返回值,即 aa = "foo"
  }catch(error){
      console.log(error,"error")
  }
}
const generator = fun1();
// 调用fun1函数不会并不会立即去执行这个函数,而是得到一个生成器对象
console.log(generator,"generator") 
const result = generator.next();
// 直到我们手动调用这个对象的next方法,这个函数的函数体才会开始执行
// 在next方法返回的对象{value: "foo", done: false},去拿到yeild返回的值
// next方法返回的对象里面的done属性表示这个生成器是否已经全部执行完了
console.log(result,"result")
// 调用next传入了参数的话,传入的参数会作为yield语句的返回值
 generator.next("bar") 
// throw方法也是让函数继续向下执行,只不过生成器函数内部抛出一个异常,要用try,catch去捕获
generator.throw("报错啦")

Let's look at an example

//ajax("http://www.baidu.com")函数这里是一个伪代码,假设返回一个promise对象
function ajax(){
  return new Promise(...)
}
function* main(){
  const data1 = yeild ajax("http://www.baidu1.com")  
  console.log(data1)
  const data2 = yeild ajax("http://www.baidu2.com")  
  console.log(data2)
}
const g = main()
const result = g.next()  //这里result就是一个生成器对象,value值是yeild ajax("http://www.baidu.com")返回的promise对象
result.value.then((value)=>{
  const result1 = g.next(value)  // 把promise执行完返回的值,传给data1
  if(result1.done) return
  result1.value.then((value)=>{
      let result2 = g.next(value)  // 把promise执行完返回的值,传给data2
      if(result3.done) return
       // ...如此往复,可以使用递归的方式
  })
})

Therefore, you can get the result in the generator function body, so that you can use synchronous writing to solve asynchronous problems

Encapsulate a generator function executor ( https://github.com/tj/co)

function co(generator){
    const g = generator()
    function handleResult(result){
        if(result.done) return
        result.value.then((data)=>{
            handleResult(g.next(data))
        },(error)=>{
            g.throw(error) // 外部用try catch去捕获
        })
    }
    handleResult(g.next())
}
// 调用 co(main)

async await

  • Async await is the syntactic sugar of generator. Compared with generator, async await does not need cooperate with an executor similar to co. The internal execution process is exactly the same as that of generator.
  • The async function returns a promise object
  • await can only be used in async functions

    function* fun() {
    let a = yield setTimeout(() => console.log("111", 0));
    let b = yield setTimeout(() => console.log("222", 0));
    }
    /**
     * 等价于
     */
    async function fun() {
    let a = await setTimeout(() => console.log("111", 0));
    let b = await setTimeout(() => console.log("222", 0));
    }

    So now we will use async await in most cases, it is an almost synchronous way to achieve asynchronous


LeapFE
1.1k 声望2.3k 粉丝

字节内推,发送简历至 zhengqingxin.dancing@bytedance.com