深刻理解、实现promise、async/await

promise、async/await的提出,已由来已久,不少人都知道这两个都是为了解决回调地狱问题的,面试中不少面试官都喜欢那这两个做比较,那我今天就来讲说promise、async/await的区别以及简单的原理实现。避免面试的尴尬!git

篇幅比较长,理解和实现两部分(主要在实现),最近看到不少面试的同窗都有提到手写promise、async/await的问题,就随手整理了一下,以前也整理了一下唠一唠call、apply和bind以及手动实现(拒绝晦涩难懂)github

promise

Promise,简单来讲就是一个容器,里面保存着某个将来才会结束的时间(一般是一个异步操做的结果),经过链式调用同步实现异步;面试

特色npm

  • 对象的状态不受外界影响;
  • 链式调用(依旧没逃出回调的问题)
  • 异常处理(catch
  • 状态一旦改变,就不会再变,状态凝固;

具体的能够关注下关于Promise以及你可能不知道的6件事 以及Promise 大坑避让准则编程

Promise/A+实现

这篇剖析Promise内部结构写的很不错,这里也分享给你们,面试当中写个很简单的就能够了,对于源码咱们理解便可;promise

实现Promise的要求:bash

  • 构造一个Promise实例须要给Promise构造函数传入一个函数,传入的函数须要有两个形参,即resolvereject注意两个形参都是function类型的参数;
  • Promise上还有then方法(多个then可造成链式调用),then方法就是用来指定Promsie对象的状态改变肯定执行的操做,resolve时执行第一个函数(onFulfilled), reject时执行第二个函数(onRejected);
  • 当状态变为resolve后就不能在改变成reject, 反之同理;

基于以上要求咱们能够实现一个简单的Promise:app

// promise 三个状态
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

class myPromise {
    constructor(executor) {
        this.status = PENDING; // 声明初始状态;
        this.value = undefined; // fulfilled状态时 返回的信息
        this.reason = undefined; // rejected状态时 拒绝的缘由;
        this.onFulfilledCallbacks = []; // 存储fulfilled状态对应的onFulfilled函数
        this.onRejectedCallbacks = []; // 存储rejected状态对应的onRejected函数
        //=>执行Excutor
        let resolve = result => { // resolve方法
            if (this.status !== PENDING) return;

            // 为何resolve 加setTimeout?
            // 2.2.4规范 onFulfilled 和 onRejected 只容许在 execution context 栈仅包含平台代码时运行.
            // 这里的平台代码指的是引擎、环境以及promise的实施代码。实践中要确保onFulfilled 和 onRejected方法异步执行,且应该在then方法被调用的那一轮事件循环以后的新执行栈中执行。

            setTimeout(() => {
                //只能由pending状态=>fulfilled状态(避免调用屡次resolve reject)
                this.status = FULFILLED;
                this.value = result;
                this.onFulfilledCallbacks.forEach(cb => cb(this.value));
            }, 0);
        };
        let reject = reason => { // reject方法
            if (this.status !== PENDING) return;
            setTimeout(() => {
                this.status = REJECTED;
                this.reason = reason;
                this.onRejectedCallbacks.forEach(cb => cb(this.reason))
            })
        };
        // 捕获在executor执行器中抛出的异常
        try {
            executor(resolveFn, rejectFn);
        }
        catch (err) {
            //=>有异常信息按照rejected状态处理
            reject(err);
        }
    }

    // 添加.then方法
    then(onFullfilled, onRejected) {
        this.onFulfilledCallbacks.push(onFullfilled);
        this.onRejectedCallbacks.push(onRejected);
    }

    // 添加.catch方法
    catch(onRejected) {
        return this.then(null, onRejected);
    }
}

module.exports = myPromise;
复制代码

不过在面试中有些会要求手写一个符合Promises/A+规范的完美Promise。异步

首先来看下Promises规范:async

Promise 规范有不少,如Promise/A,Promise/B,Promise/D 以及 Promise/A 的升级版 Promise/A+。ES6 中采用了Promise/A+ 规范。

// promise 三个状态
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

class myPromise {
    constructor(executor) {
        this.status = PENDING; // 声明初始状态;
        this.value = undefined; // fulfilled状态时 返回的信息
        this.reason = undefined; // rejected状态时 拒绝的缘由;
        this.onFulfilledCallbacks = []; // 存储fulfilled状态对应的onFulfilled函数
        this.onRejectedCallbacks = []; // 存储rejected状态对应的onRejected函数
        //=>执行Excutor
        let resolve = result => { // resolve方法
            if (this.status !== PENDING) return;

            // 为何resolve 加setTimeout?
            // 2.2.4规范 onFulfilled 和 onRejected 只容许在 execution context 栈仅包含平台代码时运行.
            // 这里的平台代码指的是引擎、环境以及promise的实施代码。实践中要确保onFulfilled 和 onRejected方法异步执行,且应该在then方法被调用的那一轮事件循环以后的新执行栈中执行。

            setTimeout(() => {
                //只能由pending状态=>fulfilled状态(避免调用屡次resolve reject)
                this.status = FULFILLED;
                this.value = result;
                this.onFulfilledCallbacks.forEach(cb => cb(this.value));
            }, 0);
        };
        let reject = reason => { // reject方法
            if (this.status !== PENDING) return;
            setTimeout(() => {
                this.status = REJECTED;
                this.reason = reason;
                this.onRejectedCallbacks.forEach(cb => cb(this.reason))
            })
        };
        // 捕获在executor执行器中抛出的异常
        try {
            executor(resolveFn, rejectFn);
        }
        catch (err) {
            //=>有异常信息按照rejected状态处理
            reject(err);
        }
    }

    // 添加.then方法
    then(onFullfilled, onRejected) {
        onFullfilled = typeof onFullfilled === "function" ? onFullfilled : value => value;
        onRejected = typeof onRejected === "function" ? onRejected : reason => {
            throw reason;
        };
        switch (self.status) {
            case PENDING:
                // 当异步调用resolve/rejected时 将onFulfilled/onRejected收集暂存到集合中
                return promise2 = new myPromise((resolve, reject) => {
                    this.onFulfilledCallbacks.push(()=>{
                        try {
                            let x = onFullfilled(this.value);
                            this.resolvePromise(promise2, x, resolve, reject); // 新的promise resolve 上一个onFulfilled的返回值
                        }
                        catch (e) {
                            reject(e); // 捕获前面onFulfilled中抛出的异常 then(onFulfilled, onRejected);
                        }
                    });
                    this.onRejectedCallbacks.push(() => {
                        try {
                            let x = onRejected(this.reason);
                            this.resolvePromise(promise2, x, resolve, reject);
                        }
                        catch (e) {
                            reject(e); // error catch
                        }
                    });
                });
                break;
            case FULFILLED:
                return promise2 = new myPromise(function (resolve, reject) {
                    try {
                        let x = onFullfilled(this.value);
                        //将上次一then里面的方法传递进下一个Promise状态
                        this.resolvePromise(promise2, x, resolve, reject);
                    }
                    catch (e) {
                        reject(e);//error catch
                    }
                });
                break;
            case REJECTED:
                return promise2 = new myPromise(function (resolve, reject) {
                    try {
                        let x = onRejected(this.reason);
                        //将then里面的方法传递到下一个Promise的状态里
                        this.resolvePromise(promise2, x, resolve, reject);
                    }
                    catch (e) {
                        reject(e);
                    }
                });
                break;
            default:
        }
    }

    // 添加.catch方法
    catch(onRejected) {
        return this.then(null, onRejected);
    }

    static deferred() { // 延迟对象
        let defer = {};
        defer.promise = new myPromise((resolve, reject) => {
            defer.resolve = resolve;
            defer.reject = reject;
        });
        return defer;
    }

    static all(promises = []) {
        let index = 0,
            result = [];
        return new Promise((resolve, reject) => {
            for (let i = 0; i < promises.length; i++) {
                promises[i].then(val => {
                    index++;
                    result[i] = val;
                    if (index === promises.length) {
                        resolve(result)
                    }
                }, reject);
            }
        })
    }
    
    static resolvePromise(promise, x, resolve, reject) {
        if (promise === x) { // 若是从onFulfilled中返回的x 就是promise2 就会致使循环引用报错
            throw new TypeError("type error")
        }
        let isUsed;
        if (x !== null && (x instanceof Object || x instanceof Function)) {
            try {
                let then = x.then;
                if (typeof then === "function") {
                    //是一个promise的状况
                    then.call(x,(y) => {
                        if (isUsed) return;
                        isUsed = true;
                        this.resolvePromise(promise, y, resolve, reject);
                    },(e) => {
                        if (isUsed) return;
                        isUsed = true;
                        reject(e);
                    })
                }
                else {
                    //仅仅是一个函数或者是对象
                    resolve(x)
                }
            }
            catch (e) {
                if (isUsed) return;
                isUsed = true;
                reject(e);
            }
        }
        else {
            //返回的基本类型,直接resolve
            resolve(x)
        }
    }
}

module.exports = myPromise;
复制代码

Promise测试

npm i -g promises-aplus-tests

promises-aplus-tests Promise.js
复制代码

promise的原理Promise/A+的实现就结束了。


async/await

  • async/await更加语义化,async 是“异步”的简写,async function 用于申明一个 function 是异步的; await,能够认为是async wait的简写, 用于等待一个异步方法执行完成;

  • async/await是一个用同步思惟解决异步问题的方案(等结果出来以后,代码才会继续往下执行)

  • 能够经过多层 async function的同步写法代替传统的callback嵌套,规避掉了Promise的链式调用,代码看起来简单明了;

栗子

function sleep(wait) {
    return new Promise((res,rej) => {
        setTimeout(() => {
            res(wait);
        },wait);
    });
}

async function demo() {
    let result01 = await sleep(100);
    //上一个await执行以后才会执行下一句
    let result02 = await sleep(result01 + 100);
    let result03 = await sleep(result02 + 100);
    // console.log(result03);
    return result03;
}

demo().then(result => {
    console.log(result);
});
复制代码

异步编程的最高境界,根本就不用关心它异步,不少人认为它是异步操做的终极解决方案;可是和Promise不存在谁取代谁,由于async/await是寄生于PromiseGenerator的语法糖。

Generator还比较陌生的同窗能够看下廖雪峰写的generator,这里就不作过多介绍了。

实现一个简单的async/await

async/await语法糖就是使用Generator函数+自动执行器来运做的。 咱们能够参考如下例子

// 定义了一个promise,用来模拟异步请求,做用是传入参数++
function getNum(num){
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(num+1)
        }, 1000)
    })
}

//自动执行器,若是一个Generator函数没有执行完,则递归调用
function asyncFun(func){
  var gen = func();

  function next(data){
    var result = gen.next(data);
    if (result.done) return result.value;
    result.value.then(function(data){
      next(data);
    });
  }

  next();
}

// 所须要执行的Generator函数,内部的数据在执行完成一步的promise以后,再调用下一步
var func = function* (){
  var f1 = yield getNum(1);
  var f2 = yield getNum(f1);
  console.log(f2) ;
};
asyncFun(func);
复制代码

在执行的过程当中,判断一个函数的promise是否完成,若是已经完成,将结果传入下一个函数,继续重复此步骤。


结束

若是发现过程遇到什么问题,欢迎及时提出

相关文章
相关标签/搜索