手撕面试官...啊,不,手撕Promise

486 阅读4分钟

闲来无事,想来一个面试题系列,先来一个最经典的手写的一个Promise,你会吗?我不会,所以我来写一个。。。

1. Promise详解

想手写实现一个Promise先要知道Promise的原理和他内部的流程,就是重要知道自己要写什么,就和写业务拆分页面,拆解组件一个道理。 我们熟知的Promise就是一个异步对象,可以传入一个带有resolve, reject参数的函数如下:

const pro = new Promise((res, rej) => {
  console.log('promise执行了')
})

Promise里还有自己的状态,Padding 为初始的状态,Fulfilled 为成功的状态,Rejected 为失败的状态,这里的状态是不可逆的,就是一个Promise对象状态一旦发生改变就不会再转换回去,这里其实就为我们后续调用的函数提供了保证。(PS: Promise对象里的代码是同步执行的)

不同状态有与之对应的实例方法来执行后续的逻辑,promise.then():获取异步任务的正常结果,promise.catch():获取异步任务的异常结果,promise.finaly():异步任务无论成功与否,都会执行。

const pro = new Promise((res, rej) => {
  console.log('promise执行了')
  res('成功')
  // rej('失败')
})
pro
  .then(res => {
    console.log(res)
  })
  .catch(rej => {
    console.log(rej)
  })
  .finally(() => {
    console.log('完成')
  })

Promise还有一个很重要的链式调用,就是Promise的返回都是一个Promise对象,可以继续执行后续的状态方法的调用。如果逻辑需要中断可以在需要中断的地方手动的抛出一个throw错误来中断链式调用。

还有两个使用的方法, promise.all (): 并发处理多个异步任务,所有任务都执行成功,才能得到结果 promise.race (): 并发处理多个异步任务,只要有一个任务执行成功,就能得到结果

以上就是Promise的内容,也可以说是这次的功能点拆分,下面就是咱们自己手写一个Promise的环节了。

2. 手写Promise

  • 首先我们写一个myPromise类,先实现传进一个函数参数,传入就立即执行
class myPromise{
  constructor(immediate) {
    // 成功
    let resolve = () => {}
    // 失败
    let reject = () => {}
    // 立即执行函数
    immediate()
  }
}
  • 接着定义myPromise里的三个状态pengding, fulfilled, rejected
class myPromise {
  constructor(immediate) {
    this.state = 'pending'
    this.value // 成功时的值
    this.error // 失败时的值
    // 成功
    let resolve = (res) => {
      if (this.state === 'pending') {
        this.state = 'fufilled'
        this.value = res
      }
    }
    // 失败
    let reject = (rej) => {
      if (this.state === 'pending') {
        this.state = 'rejected'
        this.error = rej
      }
    }
    // 立即执行函数
    immediate(resolve, reject)
  }
}

new myPromise((resolve, reject) => {
  resolve('成功了')
})
// 控制台输出 Promise {state: "fufilled", value: "成功了"}
  • 接下来实现.then方法
class myPromise {
  constructor(immediate) {
    this.state = 'pending'
    this.value // 成功时的值
    this.error // 失败时的值
    // 成功
    let resolve = (res) => {
      if (this.state === 'pending') {
        this.state = 'fufilled'
        this.value = res
      }
    }
    // 失败
    let reject = (rej) => {
      if (this.state === 'pending') {
        this.state = 'rejected'
        this.error = rej
      }
    }
    // 立即执行函数
    immediate(resolve, reject)
  }

  // 声明then方法
  then (fufilledCall, rejectedCall) {
    if (this.state === 'fufilled') {
      fufilledCall(this.value)
    }
    if (this.state === 'rejected') {
      rejectedCall(this.error)
    }
  }
}

new myPromise((resolve, reject) => {
  resolve('成功了')
})
  .then(res => {
    console.log('成功回调', res)
  })
// 控制台输出 成功回调 成功了
  • 然后实现.then链式调用(完整代码)
class myPromise {
  constructor(immediate) {
    this.state = 'pending'
    this.value // 成功时的值
    this.error // 失败时的值
    // 成功
    let resolve = (res) => {
      if (this.state === 'pending') {
        this.state = 'fufilled'
        this.value = res
      }
    }
    // 失败
    let reject = (rej) => {
      if (this.state === 'pending') {
        this.state = 'rejected'
        this.error = rej
      }
    }
    // 立即执行函数
    immediate(resolve, reject)
  }

  // 声明then方法
  then (fufilledCall, rejectedCall) {
    let promise2
    promise2 = new myPromise((resolve, reject) => {
      if (this.state === 'fufilled') {
        let x = fufilledCall(this.value);
        resolvePromise(promise2, x, resolve, reject)
      }
      if (this.state === 'rejected') {
        let x = rejectedCall(this.error);
        resolvePromise(promise2, x, resolve, reject)
      }
    })
    return promise2
  }
}

/**
 * 处理promise递归的函数
 *
 * promise2 {Promise} 默认返回的promise
 * x {*} 我们自己 return 的对象
 * resolve
 * reject
 */
function resolvePromise (promise2, x, resolve, reject) {

  // 循环引用报错
  if (x === promise2) {
    // reject 报错抛出
    return reject(new TypeError('Chaining cycle detected for promise'));
  }

  // 锁,防止多次调用
  let called;

  // x 不是 null 且 x 是对象或者函数
  if (x != null && (typeof x === 'object' || typeof x === 'function')) {
    try {
      // A+ 规定,声明then = x的then方法
      let then = x.then;
      // 如果then是函数,就默认是promise了
      if (typeof then === 'function') {
        // then 执行 第一个参数是 this 后面是成功的回调 和 失败的回调
        then.call(x, y => {
          // 成功和失败只能调用一个
          if (called) return;
          called = true;
          // 核心点2:resolve 的结果依旧是 promise 那就继续递归执行
          resolvePromise(promise2, y, resolve, reject);
        }, err => {
          // 成功和失败只能调用一个
          if (called) return;
          called = true;
          reject(err);// 失败了就失败了
        })
      } else {
        resolve(x); // 直接成功即可
      }
    } catch (e) { // 走到 catch 也属于失败
      if (called) return;
      called = true;
      // 取then出错了那就不要在继续执行了
      reject(e);
    }
  } else {
    resolve(x);
  }
}

new myPromise((resolve, reject) => {
  resolve('成功了')
})
  .then(res => {
    console.log('成功回调', res)
    return new myPromise((resolve, reject) => {
      resolve('hello world');
    })
  })
  .then(res => {
    console.log('成功回调2', res)
    return new myPromise((resolve, reject) => {
      resolve('haha')
    })
  })
  .then(res => {
    console.log('成功回调3', res)
  })

这里手写一个Promise基本完成,但是每次都需要在.then里手动return一个对象出来,感觉不完美,还没想到好方法解决,在看看吧。 有好的方法也可以发出来,让我学习一下。