手写实现简单的promise

手写promise几乎每家大厂的必备考题,几回面试都被这个问题坑了,因而花了些时间特地研究了一下,下面是promise实现的思考过程。你们若是不嫌弃,还请往下看:面试

promise的介绍

所谓Promise,简单说就是一个容器,里面保存着某个将来才会结束的事件(一般是一个异步操做)的结果。从语法上说,Promise 是一个对象,从它能够获取异步操做的消息。 例如如下一个promise的例子:promise

const promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* 异步操做成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});
复制代码

能够看出一个promise的构造函数包含两个方法resolve、reject,同时根据promise+规范可知promise包含三个状态:bash

  • pending: 初始状态,既不是成功,也不是失败状态。
  • fulfilled: 意味着操做成功完成。
  • rejected: 意味着操做失败。 那么咱们能够能够根据这三种不一样状态去实现resolve、reject,以及实现then方法,那么一个简单的promise雏形就出来了。下面来实现它:

promise构造函数:

首先能够根据上面的推测写个构造函数以下:闭包

/**
 * 建立三变量记录表示状态
 * 用that保存this,避免后期闭包致使this的指向不对
 * value 变量用于保存 resolve 或者 reject 中传入的值
 * resolvedCallbacks 和 rejectedCallbacks 用于保存 then 中的回调,
 * 由于当执行完 Promise 时状态可能仍是等待中,这时候应该把 then 中的回调保存起来用于状态改变时使用
 */

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

function myPromise(fn){
  const that = this;
  that.value = null;
  that.status = PENDING; //默认状态
  that.fulfilledCallbacks = [];
  that.rejectedCallbacks = [];
  function resolve(value){
    if(that.status === PENDING ){
      }
  }
 function reject(value){
    if(that.status === PENDING ){
      }
  }
    
  // 执行回调函数
   try{
        fn(resolve, reject)
    }catch (e) {
        reject(e);
    }
}
复制代码

因而思考当在resolve里该干些什么?resolve即执行状态,首先status状态值得变吧,改为fulfilled状态,同时将传入的value值保存起来,以便下面的then会用到,最后得执行回调里面的方法实现回调调用。reject同理,因而按这个思路,就有了:异步

/**
 * 建立三变量记录表示状态
 * 用that保存this,避免后期闭包致使this的指向不对
 * value 变量用于保存 resolve 或者 reject 中传入的值
 * resolvedCallbacks 和 rejectedCallbacks 用于保存 then 中的回调,
 * 由于当执行完 Promise 时状态可能仍是等待中,这时候应该把 then 中的回调保存起来用于状态改变时使用
 */

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

function myPromise(fn){
  const that = this;
  that.value = null;
  that.status = PENDING; //默认状态
  that.fulfilledCallbacks = [];
  that.rejectedCallbacks = [];
  function resolve(value){
    if(that.status === PENDING ){
        that.status = FULFILLED;
        that.value = value;
        //执行回调方法
        that.fulfilledCallbacks.forEach(myFn => myFn(that.value))
      }
  }
 function reject(value){
    if(that.status === PENDING ){
        that.status = REJECTED;
        that.value = value;
        //执行回调方法
        that.rejectedCallbacks.forEach(myFn => myFn(that.value))
      }
  }
    
  // 执行回调函数
   try{
        fn(resolve, reject)
    }catch (e) {
        reject(e);
    }
}
复制代码

因而promise的构造函数就简陋的完成了,接下来实现then不就大工完成了吗?是否是有些小兴奋~~~函数

promise中then的实现

考虑到全部的实例都要用到then方法,在then得放在promise的原型链上。当状态是PENDING状态时,该作什么?不执行回调,那就将回调方法分别放入不一样的栈内,等待调用。当状态为FULFILLED或者REJECTED时,则执行响应的方法便可。因而:post

myPromise.prototype.then = function (onFulfilled, onRejected){
    let self = this;
    //等待状态,则添加回调函数到栈中
    if(self.status === PENDING){
        self.fulfilledCallbacks.push(()=>{
            onFulfilled(self.value);
        });
        self.rejectedCallbacks.push(()=>{
            onRejected(self.value);
        })
    }
    if(self.status === FULFILLED){
        onFulfilled(self.value);
    }

    if(self.status === REJECTED){
        onRejected(self.value)
    }
}
复制代码

因而一个简单的promise实现以下:优化

/**
 * 建立三变量记录表示状态
 * 用that保存this,避免后期闭包致使this的指向不对
 * value 变量用于保存 resolve 或者 reject 中传入的值
 * resolvedCallbacks 和 rejectedCallbacks 用于保存 then 中的回调,
 * 由于当执行完 Promise 时状态可能仍是等待中,这时候应该把 then 中的回调保存起来用于状态改变时使用
 */

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

function myPromise(fn){
  const that = this;
  that.value = null;
  that.status = PENDING; //默认状态
  that.fulfilledCallbacks = [];
  that.rejectedCallbacks = [];
  function resolve(value){
    if(that.status === PENDING ){
        that.status = FULFILLED;
        that.value = value;
        //执行回调方法
        that.fulfilledCallbacks.forEach(myFn => myFn(that.value))
      }
  }
 function reject(value){
    if(that.status === PENDING ){
        that.status = REJECTED;
        that.value = value;
        //执行回调方法
        that.rejectedCallbacks.forEach(myFn => myFn(that.value))
      }
  }
    
  // 执行回调函数
   try{
        fn(resolve, reject)
    }catch (e) {
        reject(e);
    }
}
 myPromise.prototype.then = function (onFulfilled, onRejected){
    let self = this;
    //等待状态,则添加回调函数到栈中
    if(self.status === PENDING){
        self.fulfilledCallbacks.push(()=>{
            onFulfilled(self.value);
        });
        self.rejectedCallbacks.push(()=>{
            onRejected(self.value);
        })
    }
    if(self.status === FULFILLED){
        onFulfilled(self.value);
    }

    if(self.status === REJECTED){
        onRejected(self.value)
    }
}

let p = new thePromise((resolve, reject)=>{
    console.log('hello');
    resolve(5);
});
p.then((res)=>{
    console.log(res);
})
p.then(()=>{
    console.log('jj');
})
复制代码

结果以下: ui

image.png
因而一个简单的promise大工告成!

但是有没有发现then里并无返回一个promise,并不符合规范,因此能够对promise进行部分优化。this

改造promise

根据promise+规范改造promise,传给then的应该是个promise,以下: resolve 和reject的改造:

  • 对于 resolve 函数来讲,首先须要判断传入的值是否为 Promise 类型
  • 为了保证函数执行顺序,须要将两个函数体代码使用 setTimeout 包裹起来
function resolve(value) {
        if(value instanceof myPromise){
            return value.then(resolve, reject);
        }
        setTimeout(()=>{
            that.status = FULFILLED;
            that.value = value;
            //执行会回调方法
            that.fulfilledCallbacks.forEach(myFn => myFn(that.value))
        },0)
    }

    function reject(value) {
        setTimeout(()=>{
            that.status = REJECTED;
            that.value = value;
            //执行会回调方法
            that.rejectedCallbacks.forEach(myFn => myFn(that.value))
        }, 0);
    }
复制代码

接下来改造then方法: 首先咱们须要新增一个变量 promise2,由于每一个 then 函数都须要返回一个新的 Promise 对象,该变量用于保存新的返回对象,因而:

myPromise.prototype.then = function (onFulfilled, onRejected) {
    let self = this;
    let promise2 = null;
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
    onRejected = typeof onRejected === 'function' ? onRejected : r => {throw r}
    //等待状态,则添加回调函数到栈中
    if(self.status === PENDING){
        return (promise2 = new myPromise((resolve, reject)=>{
            self.fulfilledCallbacks.push(()=>{
                try {
                    let x = onFulfilled(self.value);
                    resolutionProduce(promise2, x, resolve, reject);
                }catch (e) {
                    reject(e)
                }
            });
            self.rejectedCallbacks.push(()=>{
                try {
                    let x = onRejected(self.value);
                    resolutionProduce(promise2, x, resolve, reject);
                }catch (e) {
                    reject(e);
                }
                onRejected(self.value);
            });
        }));
    }
    if(self.status === FULFILLED){
        return(promise2 = new myPromise((resolve, reject)=>{
            setTimeout(()=>{
                try {
                    let x = onFulfilled(self.value);
                    resolutionProduce(promise2, x, resolve, reject);
                }catch (e) {
                    reject(e);
                }
            }, 0)
        }));
    }

    if(self.status === REJECTED){
        return (promise2 = new myPromise((resolve, reject)=>{
            setTimeout(()=>{
                try {
                    let x = onRejected(self.value);
                    resolutionProduce(promise2, x, resolve, reject)
                }catch (e) {
                    reject(e);
                }
            },0)
        }));
    }
}
复制代码

思路跟以前基本一致,只是说把以前返回值改为promise,同时捕获异常,在status状态为FULFILLED或者REJECTED的时候执行得加上异步setTimeout包裹。 接下来完成最核心的resolutionProduce函数:

  • 首先规范规定得保证当前的x不能与promise2一致,不然将执行无心义的相同操做,致使循环引用的发生。 例如:
let p = new myPromise((resolve, reject) => {
  resolve(1)
})
let p1 = p.then(value => {
  return p1
})
复制代码
  • 而后须要判断 x 的类型
if (x instanceof MyPromise) {
    x.then(function(value) {
        resolutionProcedure(promise2, value, resolve, reject)
    }, reject)
}
复制代码

这里的代码是彻底按照规范实现的。若是 x 为 Promise 的话,须要判断如下几个状况:

  1. 若是 x 处于等待态,Promise 需保持为等待态直至 x 被执行或拒绝
  2. 若是 x 处于其余状态,则用相同的值处理 Promise
function resolutionProduce(promise, x, resolve, reject){
 if(promise === x){
        return reject(new TypeError('Error'));
 }
 let called = false;
 if(x !== null && (typeof x === 'object' || typeof x === 'function')){
        try {
            let then = x.then;
            if(typeof then === 'function'){
                then.call(x, y=>{
                    if(called) return;
                    called = true;
                    resolutionProduce(promise2, y, resolve, reject )
                }, e =>{
                    if(e) return;
                    called = true;
                    reject(e);
                })
            }else {
                resolve(x);
            }
        }catch (e) {
            if(called) return;
            called = true;
            reject(e);
        }
    }else {
        resolve(x);
    }
}
复制代码
  • 首先建立一个变量 called 用于判断是否已经调用过函数
  • 而后判断 x 是否为对象或者函数,若是都不是的话,将 x 传入 resolve 中
  • 若是 x 是对象或者函数的话,先把 x.then 赋值给 then,而后判断 then 的类型,若是不是函数类型的话,就将 x 传入 resolve 中
  • 若是 then 是函数类型的话,就将 x 做为函数的做用域 this 调用之,而且传递两个回调函数做为参数,第一个参数叫作 resolvePromise ,第二个参数叫作 rejectPromise,两个回调函数都须要判断是否已经执行过函数,而后进行相应的逻辑
  • 以上代码在执行的过程当中若是抛错了,将错误传入 reject 函数中

以上即是promise的简单实现,下回有时间将把catch与all、race等方法实现,敬请期待 未完待续。。。 参考: juejin.im/post/5b88e0…

相关文章
相关标签/搜索