手写一个Promise(最详细)

79 阅读5分钟

什么是Promise

Promise是用来解决异步问题的一种方案。

原理:用构造函数将异步操作进行封装,在异步执行结束后,根据任务的成功或失败改变Promise的状态,在Promise实例对象的then方法中可获取异步操作的值。

为什么要用Promise

1.Promise之前的异步是如何实现的?

是通过纯回调函数实现的:

getFile(syncFunc,successCallback,errorCallback);

多个串行且相互依赖的异步操作如下:

// 回调地狱
getFile(data,successCallback(result1){
    getFileTwo(result1,successCallbackTwo(result2){
        getFileThree(result2,successCallbackThree(result3){
            // 处理数据
        },errorCallback3)
    },errorCallback2)
},errorCallback1)

可以看出,回调方式的异步串行会造成回调地狱,很难维护,并且错误处理与正常业务代码耦合在一起,牵一发而动全身。

如何使用promise

在使用promise的同时,我们顺便用代码手写一个Promise,加深理解。

基本使用
// 将异步封装在promise构造函数的执行器当中
const promise1 = new Promise((resolve,reject)=>{
    setTimeout(()=>{
        resolve('success')
        // 测试状态是否可被改变
        reject('error')
    },1000)
})
// 用then方法获取异步数据
const promise2 = promise1.then(value=>{
    console.log('第一次获取异步数据',value);
    throw new Error('error')
},reason=>{
    console.log('第一次获取异步数据',reason);
})

promise2.then(res=>{
    console.log('第二次获取异步数据',res);
},reason=>{
    console.log('第二次获取异步数据',reason);
})


//结果 1s后控制台打印
'第一次获取异步数据','success'
'第二次获取异步数据','Failed:error'

以上代码证明了promise的基本功能点:

1.promise状态不可变性。resolve和reject可以将promise的状态从pending变为成功或者失败,一旦状态改变不可逆。

2.promise支持异步操作。异步结束会根据异步操作的结果(promise状态)执行then中相应的回调函数。

//基础promise 自实现

class Promise {
  constructor(excutor){
    // 状态
    this.status = 'pending';
    // 存放成功的值
    this.value = '';
    // 存放失败的理由
    this.reason = '';
    // 存放成功后的回调
    this.resolvedCbs = [];
    // 存放失败后的回调
    this.rejectedCbs = [];

    let resolve = (value) => {
      // 防止调用两次resolve reject
      if(this.status === 'pending'){
        this.status = 'resolved';
        this.value = value;
        // setTimeout使回调异步执行
        setTimeout(() => {
          this.resolvedCbs.forEach(fn=>fn())
        }, 0);
        
      }
    }

    let reject = (reason) => {
      // 防止调用两次resolve reject
      if(this.status === 'pending'){
        this.status = 'rejected';
        this.reason = reason;
        // setTimeout使回调异步执行
        setTimeout(() => {
          this.rejectedCbs.forEach(fn=>fn())
        }, 0);
        
      }
    }

    try{
      // 创建实例的时候 同步执行执行器函数
      excutor(resolve,reject);
    } catch (err){
      // 执行器抛出错误 表示promise失败
      reject(err)
    }
  }

  then(onResolved,onRejected){
  
      if(this.status === 'resolved'){
        onResolved(this.value)
      }

      if(this.status === 'rejected'){
        onRejected(this.reason)
      }

       // 这里是实现异步的关键 异步操作还没完成时调用then,会将回调函数先存起来,状态改变之后再依次调用
      if(this.status === 'pending'){
        this.resolvedCbs.push(onResolved)
        this.rejectedCbs.push(onRejected)
      }
}

以上,我们已经实现了一个简单的promise,包含异步操作和then获取。

promise最出色的部分在于它用链式结构解决了回调地狱,所以必须要实现then的链式调用,这就要求then方法中要返回一个promise

// promise 链式调用案例
const promise1 = new Promise((resolve,reject)=>{
  resolve('成功1')
})

const promise2 = promise1.then(res=>{
  console.log('第一次结果',res)
  return new Promise((resolve,reject)=>resolve('成功2'))
})

const promise3 = promise2.then(res=>{
  console.log('第二次结果',res)
  return 3
})

promise3.then(res=>{
  console.log('第三次结果',res)
})

结果:

第一次结果 成功1
第二次结果 成功2
第三次结果 3

以上使用案例需要我们手写promise时加的功能点有:

1.then函数返回一个新的promise

2.这个新的promise的结果决定了下一个then中,该执行成功回调还是失败回调

3.新的promise的结果由then回调函数的结果决定。

4.其实只是在then源码外层包了一层promise,跟then本身返回的promise/函数/undefined没有任何关系

5.then中的回调函数执行结果,直接影响then外层包裹的promise的状态。


class Promise {
	constructor(executor){
		this.value = undefined;
		this.reason = undefined;
    this.status = 'PENDING';
    this.onResolveCallbacks = [];
    this.onRejectCallbacks = [];

  
		try{
      executor(this.resolve,this.reject);
    } catch(err) {
      this.reject(err)
    }

	}

  resolve = (value) => {
    // 防止调用两次resolve/reject
    if(this.status === 'PENDING'){
      this.value = value;
      this.status = 'RESOLVED';
      // 发布通知
      this.onResolveCallbacks.forEach(fn=>fn(this.value))
    }
      
  }
  // 防止调用两次resolve/reject
  reject = (reason) => {
    if(this.status === 'PENDING'){
      this.reason = reason;
      this.status = 'REJECTED'; 
      // 发布通知
      this.onRejectCallbacks.forEach(fn=>fn(this.reason))
    }  
  }

  catch(onrejected){
    this.then(undefined,onrejected)
  }

  then(onfulfilled,onrejected){

    // 以下两句是为了实现结果透传 then().then()
    onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : value => value;
    onrejected = typeof onrejected === 'function' ? onrejected : error => {throw error}

    const promise2 = new Promise((resolve,reject)=>{

      function handlePromise(x){
        let called;
        /*
        为什么不能用x.then instanceof Promise实现?
        使得我们定义的promise更具兼容性
        */
        if((typeof x === 'object' && x !== null) || typeof x === 'function'){
          if(typeof x.then === 'function'){
            x.then(value=>{
              // 防止调用两次resolve/reject
              if(called) return;
              called = true;
              handlePromise(value)
            },reason=>{
              if(called) return;
              called = true;
              reject(reason)
            })
          } else {
            resolve(x)
          }
        } else {
          if(called) return;
          called = true;
          resolve(x)
        }
      }

      if(this.status === 'RESOLVED'){
        /* 为什么要加setTimeout?
        * then中的方法是异步执行的,这里用setTimeout代替实现异步效果
        */
        setTimeout(() => {
          try {
            // 执行onfulfilled的时候可能会直接抛出错误 reject
            const x = onfulfilled(this.value);
             handlePromise(x);
          } catch (error) {
            reject(error)
          }
        }, 0);
      }
      
      if(this.status === 'REJECTED'){
        setTimeout(() => {
          try {
            const x = onrejected(this.reason);
            handlePromise(x);
          } catch (error) {
            reject(error)
          }
          
        }, 0);
      }

      if(this.status === 'PENDING'){
        // 实现订阅的功能
        this.onResolveCallbacks.push(()=>{
          setTimeout(() => {
            try {
              const x = onfulfilled(this.value);
              handlePromise(x);
            } catch (error) {
              reject(error)
            }
          }, 0);
        });
        
        this.onRejectCallbacks.push(()=>{
          setTimeout(() => {
            try {
              const x = onrejected(this.reason);
              handlePromise(x);
            } catch (error) {
              reject(error)
            }
          }, 0);
        });
      }
    })
    return promise2;
    
  }
}

Promise.resolve = (x) => {
  /*
  规则:将对象转换成promise对象
  1.处理promise实例 原封不动返回实例
  2.处理thenable对象或者函数 执行then函数
  3.处理其他
  */

  return new Promise((resolve,reject)=>{
    
    if((typeof x === 'object' && x != null) || typeof x === 'function'){
      let then = x.then;
      if(typeof then === 'function'){
        then.call(x,v=>resolve(v),reject)
      } else {
        resolve(x)
      }
    } else {
      resolve(x)
    }
  })
}

Promise.reject = (x) => {
  /*
  规则:返回一个状态为rejected状态的promise对象
  不管是啥都reject
  */

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

Promise.all = (promises) => {
  /*
  规则:返回一个状态为rejected状态的promise对象
  不管是啥都reject
  */

  return new Promise((resolve,reject)=>{
    let promiseArr = [];
    promises.forEach(x=>{

      if((typeof x === 'object' && x != null) || typeof x === 'function'){
        let then = x.then;
        if(typeof then === 'function'){
          then.call(x,value=>{
            promiseArr.push(value)

            if(promiseArr.length === promises.length) {
              resolve(promiseArr)
            }
          },reason=>{
            reject(reason);
          })
        } else {
          promiseArr.push(x)
        }
      } else {
        promiseArr.push(x)
      }

      if(promiseArr.length === promises.length) {
        resolve(promiseArr)
      }

    })
  })
}

Promise.race = (promises) => {
  return new Promise((resolve,reject)=>{
    for (let x = 0; x < promises.length; x++) {
      if((typeof x === 'object' && x != null) || typeof x === 'function'){
        let then = x.then;
        if(typeof then === 'function'){
          then.call(x,resolve,reject)
        } else {
          resolve(x)
        }
      } else {
        resolve(x)
      }
    }
  })
}

以上,手写实现了一个完整的promise,可以通过promise/A+的所有测试用例。

` 1.Promise相当于一个容器,存放异步或同步操作

2.返回的promise是一个对象,里面存放了promise成功/失败的信息

`