Promise原理、以及Promise.race、Promise.all、Promise.resolve、Promise.reject实现;

为了向那道光亮奔过去,他敢往深渊里跳;

于是今天朝着Promise的实现前进吧,写了四个小时,终于完结撒花;

我知道大家没有耐心,当然我也坐的腰疼,直接上代码,跟着我的注释一行行看过去,保证门清

javascript 复制代码
const PENDING = "pending"
const FULFILLED = "fulfilled"
const REJECTED = "rejected"
// 提前封装好一个判断promise的 这里不用instanceof 因为有可能在我们的then的参数中,有可能是别人或者自己的promise
const isPromise = value => {
  return (
    !!value &&
    (typeof value === "object" || typeof value === "function") &&
    typeof value.then === "function"
  )
}
class MyPromise {
  #state = PENDING // 当前执行状态
  #result = undefined // 当前结果值
  #handler = [] // 记录成功与失败回调的数组
  constructor(executor) {
    const resolve = value => {
      // resolve之后去改变当前状态为成功 与当前成功的值
      this.#changeState(value, FULFILLED)
    }

    const reject = reason => {
      // reject之后去改变当前状态为失败 与当前错误的值
      this.#changeState(reason, REJECTED)
    }
    try {
      // 这里直接执行参数中的函数
      executor(resolve, reject)
    } catch (error) {
      console.error(error)
      // 这里try catch 错误就走reject
      reject(error)
    }
  }
  
 // 将传入的函数放到微队列中去执行
 #runMicroTask(runTask) {
   // 如果不兼容promise
    if (typeof Promise === "function") {
      return Promise.resolve().then(runTask)
    }
    // MutationObserver兼容性更好
    if (typeof MutationObserver === "function") {
      const ob = new MutationObserver(runTask)
      const node = document.createTextNode("")
      ob.observe(node, { characterData: true })
      node.data = 1
      return
    }
    // 如果是node环境
    if (process.nextTick && typeof process.nextTick === "function") {
      process.nextTick(runTask)
    }
  } 
  // 改变状态 保存此次的值 并且执行回调
  #changeState(result, state) {
    if (this.#state != PENDING) return
    this.#state = state
    this.#result = result
    this.#run()
  }
  // 这里跑每次then后的回调
  #runOne(callback, resolve, reject) {
    // 这里主要是为了模拟微任务 都是伪代码
    this.#runMicroTask(() => {
      // 如果为函数
      if (typeof callback === "function") {
        try {
          // 拿到函数的返回值
          const data = callback(this.#result)
          // 如果是promise包括别人封装的promise
          if (isPromise(data)) {
            console.log("data", data)
            // 如果是promise那就直接去执行.then 函数
            // 这里需要注意 这个resolve 是给下一个 所以这里这个resolve函数里带有上一次then返回来的值的!
            /* 可以看这里的注释来理解
      p.then(testMap.promiseTask, err => {
        console.log("第一次err err", err)
        throw "不好意思"
      })
        .then(
          res => {
            return new MyPromise(mySuc => {
              console.log("第二次", res)
                mySuc("MyPromise的值" + res)
            })
          },
          err => {
            console.log("第二次err err", err)
          }
        )
      */
            data.then(resolve, reject)
          } else {
            // 否则就自行resolve 把then(suc=>return '结果值') 就把这个data结果值给下一次调用的then传递过去
            resolve(data)
          }
        } catch (error) {
          // 不用解释了吧
          console.error(error)
          reject(error)
        }
      } else {
        // 如果不是函数 就直接执行resolve
        const settled = this.#state === FULFILLED ? resolve : reject
        settled(this.#result)
      }
    })
  }

  #run() {
    if (this.#state === PENDING) return
    /*
把下面的注释拿上来 主要为了能一次看懂不来回跳动
这里做push主要是
const p = new MyPromise之后
p.then(res=>fn1(res))
p.then(res=>fn2(res))
记录两次 fn1 与fn2
当然如果不是链式调用其实两次的拿到的回调值都是一样的
*/
    while (this.#handler.length) {
      // 这里需要注意的是resolve 与reject用的是实际then函数中传递的resolve与reject 不是当前类中的resolve
      const { onFulfilled, onRejected, resolve, reject } =
        this.#handler.shift()
      //  这里主要是为了简化代码 传递了此次
      this.#runOne(
        this.#state === FULFILLED ? onFulfilled : onRejected,
        resolve,
        reject
      )
    }
  }

  then(onFulfilled, onRejected) {
    return new MyPromise((resolve, reject) => {
      /*
这里做push主要是
const p = new MyPromise之后
p.then(res=>fn1(res))
p.then(res=>fn2(res))
记录两次 fn1 与fn2
当然如果不是链式调用其实两次的拿到的回调值都是一样的
*/
      this.#handler.push({
        onFulfilled,
        onRejected,
        resolve,
        reject,
      })
      this.#run()
    })
  }
  //catch方法的封装 catch 的话直接让它执行错误代码就好了 
  // 可不要以为有这样的代码比如Promise.catch 没有哈 都是new Promise的回调函数reject执行后的,所以这里只要让它有这个 reject方法就行了!
  catch(onRejected) {
    return this.then(undefined, onRejected)
  }

  //resolve方法的封装,凡是被static修饰的属性和方法都是静态方法和属性,只能被类名调用
  //不能被实例化对象调用.同时也不能被子类继承,换句话说它属于当前这个类的.
  static resolve(value) {
    //返回结果为Promise对象
    // 这里呢需要判断他是不是promise resolve中可能是个promise 
    return new MyPromise((resolve, reject) => {
      if (isPromise(value)) {
        value.then(
          v => {
            resolve(v)
          },
          r => {
            reject(r)
          }
        )
      } else {
        resolve(value)
      }
    })
  }

  //reject方法的封装 reject都是出错这种明确值,所以这里不需要判断 你给啥,我给下一个error给啥
  static reject(value) {
    return new MyPromise((resolve, reject) => {
      reject(value)
    })
  }
  //all方法的封装
  static all(promises) {
    const self = this
    return new MyPromise((resolve, reject) => {
      let length = promises.length // 缓存一下有多少个promise
      let count = 0 // 用于记录resolve的数量
      let values = new Array(length) // 用于存储resolve返回的值
      for (let i = 0; i < length; i++) {
        let promise = promises[i]
        // 判断数组的每一项,如果是promise,就进入then,不是就直接放进values数组中返回
        if (isPromise(promise)) {
          promise
            .then(res => {
              // 记录promise完成的数量
              count++
              // values存储每一个promise的res
              values[i] = res
              // 由于异步代码在最后执行,我们需要在then里面判断promise的完成数量,全部完成就resolve
              // 在for外面判断,是防止它全部都不是promise实例
              if (count === length) {
                resolve(values)
              }
            })
            .catch(err => {
              // 当有一个promise实例reject,我们就直接reject
              reject(err)
            })
        } else {
          // 针对不是promise实例
          count++
          values[i] = promise
        }
      }
      // 当数据的所有项都不是promise实例,我们就在这判断多一次,然后resolve
      if (count === length) {
        resolve(values)
      }
    })
  }
  //race方法的封装
  static race(promises) {
    return new MyPromise((resolve, reject) => {
      const len = promises.length
      for (let i = 0; i < len; i += 1) {
        const promise = promises[i]
        // 只要有一条成功则全部成功
        promise.then(
          res => {
            resolve(res)
          },
          error => {
            resolve(error)
          }
        )
      }
    })
  }
}
window.MyPromise = MyPromise

const test1 = new MyPromise(suc => {
  setTimeout(() => suc("成功 p"), 1001)
})
const test2 = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    reject(3)
  }, 1000)
})
const test3 = new MyPromise(suc => {
  setTimeout(() => suc("成功 test3"), 1019)
})
MyPromise.all([test2, test1, test3])
  .then(res => {
    console.log("res all", res)
  })
  .catch(res => {
    console.log("error", res)
  })
MyPromise.race([test2, test1, test3])
  .then(res => {
    console.log("res race", res)
  })
  .catch(res => {
    console.log("error", res)
  })

const catchTest = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    reject("catch 测试")
  }, 1000)
}).catch(error => {
  console.error(error)
})

const p = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve(3)
  }, 1000)
})

const testMap = {
  promiseTask: res => {
    console.log("第一次 ===>", res)
    return new Promise(suc => {
      suc("promise方式的res", res)
    })
  },
  funcTask: res => {
    console.log("第一次 ===>", res)
    return res
  },
  // 直接穿透第一次resolve 或者reject 值到下一条then函数中
  otherTask: "其他",
}

p.then(testMap.promiseTask, err => {
  console.log("第一次err err", err)
  throw "不好意思"
})
  .then(
    res => {
      return new MyPromise(mySuc => {
        console.log("第二次", res)
        mySuc("MyPromise的值" + res)
      })
    },
    err => {
      console.log("第二次err err", err)
    }
  )
  .then(
    res => {
      console.log("第三次", res)
      return "第三次的值" + res
    },
    err => {
      console.log("第三次err err", err)
    }
  )
  .then(
    res => {
      console.log("第四次", res)
    },
    err => {
      console.log("第四次err err", err)
    }
  )
相关推荐
王哲晓5 分钟前
第三十章 章节练习商品列表组件封装
前端·javascript·vue.js
理想不理想v10 分钟前
‌Vue 3相比Vue 2的主要改进‌?
前端·javascript·vue.js·面试
酷酷的阿云20 分钟前
不用ECharts!从0到1徒手撸一个Vue3柱状图
前端·javascript·vue.js
aPurpleBerry1 小时前
JS常用数组方法 reduce filter find forEach
javascript
ZL不懂前端2 小时前
Content Security Policy (CSP)
前端·javascript·面试
乐闻x2 小时前
ESLint 使用教程(一):从零配置 ESLint
javascript·eslint
我血条子呢2 小时前
[Vue]防止路由重复跳转
前端·javascript·vue.js
半开半落3 小时前
nuxt3安装pinia报错500[vite-node] [ERR_LOAD_URL]问题解决
前端·javascript·vue.js·nuxt
理想不理想v3 小时前
vue经典前端面试题
前端·javascript·vue.js