手写promise全家桶

2021-03-04  本文已影响0人  Equicy_前端开发

关于Promise的文章那么多,内容或许差不多,那我为什么还要写一篇?两个原因:一是为了让自己更深入学习;二是将自己学习的过程记录下来。下面的内容就是按照Promises/A+规范自己手写一遍代码,包括常用的几个Promise方法。如果读者认为自己手写一个Promise完全没问题,那就没有必要浪费时间;如果感觉掌握的马马虎虎,我倒建议继续往下看看。

tip: 预计看完整片文章3-5分钟

Promise在解决什么问题?

// 一般回调写法
api.get('https://test.com/person?name="equicy"', (data) => {
  if (data.code === 0) {
    api.get('https://test.com/class?id='+data.id, (data) => {
      if (data.code === 0) {
        api.get('https://test.com/school?id='+data.id, (data) => {
            console.log(data)
        })
      }
    })
  }
})

// Promise写法
function request(url) {
  return new Promise((resolve, reject) => {
    api.get(url, (data) => {
      if (data.code === 0) {
        resolve(data)
      } else {
        reject('出错啦')
      }
    })
  })
}

request('https://test.com/person?name="equicy").then(data => {
  return request('https://test.com/class?id='+data.id)
}).then(data => {
  return request('https://test.com/school?id='+data.id)
}).then(data => {
  console.log(data)
}).catch(err => {
  console.log(err)
})

Promise是什么?

Promise的写法有很多,只要满足Promises/A+这个规范,就可以叫做Promise。因为这个规范是英文的,我依照自己认为的重点和自身的理解用中文简单翻译一下如果看不懂也没有关系,因为这里的每一条,都会在接下来的手写代码中有所体现

一. promise的状态

二. then方法

三. promise的处理过程在接下来的代码表现为函数resolvePromise

遵循规范自己手写一个Promise

接下来就依照上面提到的规范来自己实现一个promise

// 首先定义三种状态为三个常量
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

// 定义一个promise类
class Promise {
  // 该构造函数接收一个执行器(函数executor),executor需要在实例化的时候立即执行。
  constructor(executor) {
    this.status = PENDING // 默认状态为待定
    this.value = undefined // 成功的返回值
    this.reason = undefined // 失败的原因

    let resolve = (val) => {
      if (this.status === PENDING) {
        this.status = FULFILLED
        this.value = val
      }
    }

    let reject = (error) => {
      if (this.status === PENDING) {
        this.status = REJECTED
        this.reason = error
      }
    }

    try {
      executor(resolve, reject)
    } catch (error) {
      reject(error)
    }
  }
  
  then(onFulfilled, onRejected) {
    // 状态为fulfilled,执行onFulfilled,传入成功的值
    if (this.state === 'fulfilled') {
      onFulfilled(this.value);
    };
    // 状态为rejected,执行onRejected,传入失败的原因
    if (this.state === 'rejected') {
      onRejected(this.reason);
    };
  }

看着似乎没什么问题,但是如果再加点料。像这样,就会有问题

const promise = new Promise((resolve, reject) => {
  setTimeout(() =>{
     resolve(1)
   })
})

promise.then((value) =>{
  console.log(value) // '',没有输出任何结果
})

考虑异步的情况

class Promise {
  // 该构造函数接收一个执行器(函数executor),executor需要在实例化的时候立即执行。
  constructor(executor) {
    this.status = PENDING // 默认状态为待定
    this.value = undefined // 成功的返回值
    this.reason = undefined // 失败的原因

    this.onResolveCallbacks = [] // 成功的回调函数集合
    this.onRejectCallbacks = [] // 失败的回调函数集合

    let resolve = (val) => {
      if (this.status === PENDING) {
        this.status = FULFILLED
        this.value = val
        this.onResolvedCallbacks.forEach(fn=>fn());
      }
    }
    let reject = (error) => {
      if (this.status === PENDING) {
        this.status = REJECTED
        this.reason = error
        this.onRejectedCallbacks.forEach(fn=>fn());
      }
    }
    try {
      executor(resolve, reject)
    } catch (error) {
      reject(error)
    }
  }
  
  then(onFulfilled, onRejected) {
    // 状态为fulfilled,执行onFulfilled,传入成功的值
    if (this.state === 'fulfilled') {
      onFulfilled(this.value);
    };
    // 状态为rejected,执行onRejected,传入失败的原因
    if (this.state === 'rejected') {
      onRejected(this.reason);
    };
    if (this.status === PENDING) {
        /*
        * 异步并且多次调用的情况
        * p = new Promise((resolve, reject) => {
        *    setTimeout(() =>{
        *      resolve(1)
        *    })
        * })
        * p.then()
        * p.then()
        */
        this.onResolveCallbacks.push(() => {
          onFulfilled(this.value)
        })

        this.onRejectCallbacks.push(() => {
          onRejected(this.reason)
        })
      }
  }

解决链式调用的问题

then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled == 'function' ? onFulfilled : _ => _
    onRejected = typeof onRejected == 'function' ? onRejected : err => { throw err }

    let promise2 = new Promise((resolve, reject) => {
      if (this.status === FULFILLED) {
        setTimeout(() => {
          try {
            let x = onFulfilled(this.value)
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        }, 0)
      }

      if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason)
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        }, 0)
      }

      if (this.status === PENDING) {
        this.onResolveCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onFulfilled(this.value)
              resolvePromise(promise2, x, resolve, reject)
              
            } catch (error) {
              reject(error)
            }
          }, 0)
        })

        this.onRejectCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onRejected(this.reason)
              resolvePromise(promise2, x, resolve, reject)
            } catch (error) {
              reject(error)
            }
          }, 0)
        })
      }
    })

    return promise2
  }

接下来就是对函数resolvePromise的实现

function resolvePromise(promise2, x, resolve, reject) {
  // 如果promise2和x指向同一个对象(会造成死循环),则抛出类型错误
  if (promise2 === x) {
    return reject(new TypeError('A promise cannot be resolved with itself')) // 禁止循环引用
  }

  let called;
  if ((typeof x === 'object' && x != null) || typeof x === 'function') {
    try {
      let then = x.then
      if (typeof then === 'function') {
      // 将x作为this,执行then。
        then.call(x, y => {
          if(called) return;
          called = true;
          resolvePromise(promise2, y, resolve, reject)
        }, r => {
          if(called) return;
          called = true;
          reject(r)
        })
      } else {
        resolve(x)
      }

    } catch (error) {
      if(called) return;
      called = true;
      reject(error)
    }
  } else {
    resolve(x)
  }
}

到这里算是大功告成,下面再实现几个常用的方法

手写Promise的几个常用方法

// promise.resolve 直接返回成功态
Promise.resolve = function(val){
  return new Promise((resolve,reject)=>{
    resolve(val)
  });
}
//promise.reject方法 直接返回失败态
Promise.reject = function(val){
  return new Promise((resolve,reject)=>{
    reject(val)
  });
}
// promise.all 所有成功才能返回成功的数组,有一个失败,则返回失败
Promise.all = function(promises) {
  return new Promise(function(resolve, reject) {
    var result = []
    var count = 0
    var promiseNum = promises.length
    if (promises.length === 0) resolve([])

    for (let i = 0; i < promiseNum; i++) {
      Promise.resolve(promises[i]).then(function(val) {
        count++
        result[i] = val
        // 这样可以保证只有全部请求完成,才能返回所有的结果
        if (count === promiseNum) resolve(result)
      }, function(error){
        reject(error)
      })
    }
  })
}

// promise.race 一旦迭代器中的某个promise解决或拒绝,返回的promise就会解决或拒绝。
Promise.race = function(promises) {
  return new Promise(function(resolve, reject){
    for (var i; i < promises.length; i++) {
      Promise.resolve(promises[i]).then(function(data){
        resolve(data)
      }, function(error) {
        reject(error)
      })
    }
  })
}

// promise.allSettled 返回所有的失败和成功的状态,并含有状态的对象 
// allSettled 结果示例
// { status: 'fulfilled', data: 3 },
// { status: 'rejected', err: 'foo' }

Promise.allSettleds = function(promises) {
  var res = []
  var count = 0
  return new Promise((resolve, reject) => {
      for (let i=0; i<promises.length; i++) {
          promises[i].then((data)=> {
              res[i] = data
              if (count === promises.length-1) {
                resolve(res)
              }
              count++
          }).catch(err=>{
              res[i] = err
              if (count === promises.length-1) {
                resolve(res)
              }
              count++
          })
      }
  })
}

欢迎批评指正或者点赞

同时也欢迎关注公众号「前端小卡」进行交流

上一篇下一篇

猜你喜欢

热点阅读