手写Promise核心代码

简介: 手写Promise核心代码

一、实现思路

要实现一个 Promise,那就得了解它的特性,以及知道它是如何执行的。

1. Promise 是如何执行的:

  1. 新建 Promise 对象 = 创建一个异步函数。
  2. 这个异步函数中带有 resolve 与 reject 两个函数形参。
  3. 执行这个异步函数内代码,并返回成功与失败状态和回调。
  4. 在 .then 中执行这个回调并返回一个新的 Promise,同时支持链式编程。

2. Promise 特性:

  • 主动抛出错误,会调用 reject() 方法回调。
  • .then 中传入非函数参数,不会报错。

3. 实现思路:

  1. 实例化 Promise 对象:使用构造函数或者 class 实现。
  2. 实例化时执行函数,并带有 resolve reject 回调:使用构造器执行成功与失败回调。
  3. 保持状态,且状态一旦改变不会更改:padding(等待) fulfilled(成功) reject(失败)。
  4. 使用 resolve 与 reject 改变当前 Promise 状态并拿到执行参数:使用两个实例方法执行。
  5. then 执行回调结果。
  6. 异步执行:使用定时器执行 .then 回调。
  7. 链式执行:让 .then 返回一个新的 Promise 来实现链式编程。
  8. 使用 try-catch 来判断代码有无主动抛出一个错误。
  9. 对 .then 中传入的参数进行非函数判断,如果不为函数则将其回调赋值为一个空对象。

二、手搓 Promise

1. 新建 Commitment 类

使用 ES6 Class 新建 Commitment 类,并在构造器中传入默认参数 func,用来执行实例化时使用的函数。

且这个函数带有 resolve 与 reject 两个回调函数作为参数。

class Commitment{
    construcotr(func){
    // 使用 bind 绑定调用时作用域
    func(this.resolve.bind(this), this.reject.bind(this))
}
  resolve(){}
  reject(){}
}

2. 使用 static 作为 Promise 状态

添加三种状态表示当前这个 Promise 的状态,并初始化当前状态为 pending。并添加 result 属性保存当前 Promise 执行的回调参数。

在 resolve 与 reject 中更改当前 Promise 状态,并将传入的参数保存起来。

class Commitment{
  static PENDING = "待定"
  static FULFILLED = "成功"
  static REJECTED = "拒绝"
  construcotr(func){
    this.status = Commitment.PENDING
    this.result = null
    func(this.resolve.bind(this), this.reject.bind(this))
  }
  resolve(result){
    if (this.status === Commitment.PENDING) {
      this.status = Commitment.FULFILLED
      this.result = result
    }
  }
  reject(result){
    if (this.status === Commitment.PENDING) {
      this.status = Commitment.REJECTED
      this.result = result
    }
  }
}

3. 使用 .then 执行回调

添加 then() 方法来执行回调,通过判断当前状态来决定执行成功还是失败,并将 result 属性传入执行。

then(onFULFILLED, onREJECTED){
  if(this.status === Commitment.FULFILLED){
    onFULFILLED(this.result)
  }else if(this.status === CCommitment.REJECTED){
    onREJECTED(this.result)
  }
}

4. 实现异步编程

其实只需要在 then() 执行回调方法的外面嵌套上一层定时器就可以了。

then(onFULFILLED, onREJECTED){
  setTimeout(() => {
    if(this.status === Commitment.FULFILLED){
      onFULFILLED(this.result)
    }else if(this.status === CCommitment.REJECTED){
      onREJECTED(this.result)
    }
  })
}

5. 实现链式编程

这个也简单,不是很难理解,其实只要将 then() 方法的执行结果用一个新的 Promise 返回出去就行了。

then(onFULFILLED, onREJECTED){
  // 返回一个新的 Promise 实现链式编程。
  return new Commitment((resolve, reject) => {
      setTimeout(() => {
        if(this.status === Commitment.FULFILLED){
          // 这里必须调用 resolve 方法包裹,诺不然新的 Promise
          // 会接收不到当前 Promise 的返回值
          resolve(onFULFILLED(this.result))
        }else if(this.status === CCommitment.REJECTED){
          //
          reject(onREJECTED(this.result))
        }
    })
  })
}

三、完善 Promise 特性

1. 添加 try-catch 判断代码有无主动抛出错误

只需要用 try-cathc 包括住 func() 的执行,并在 catch 中调用 reject() 方法就可。

constructor(func){
  try {
    func(this.resolve.bind(this), this.reject.bind(this))
  } catch (error) {
    // 如果有抛出错误,则直接调用 reject() 回调
    this.reject(error.message)
  }
}

2. 在 .then 执行前添加非函数判断

需要判断用户使用时传入的是否不是一个回调函数。

这里使用三元表达式进行一个简单的判断赋值就完全没有问题了。

then(onFULFILLED, onREJECTED) {
  return new Commitment((resolve, reject) => {
    // 判断成功回调是否不是回调函数,如果不是则直接返回 value 值
    onFULFILLED = typeof onFULFILLED === "function" ? onFULFILLED : value => value
    // 失败回调就直接报错
    onREJECTED = typeof onREJECTED === "function" ? onREJECTED : err => { throw err}
    setTimeout(() => {
      if (this.status === Commitment.FULFILLED) {
        resolve(onFULFILLED(this.result))
      } else if (this.status === Commitment.REJECTED) {
        reject(onREJECTED(this.result))
      }
    }, 0)
  })
}

四、最终代码

class Commitment{
  static PENDING = '待定';
  static FULFILLED = '成功';
  static REJECTED = '拒绝';
  constructor(func){
    this.status = Commitment.PENDING
    this.result = null
    try {
      func(this.resolve.bind(this), this.reject.bind(this))
    } catch (error) {
      this.reject(error)
    }
  }
  resolve(result){
    if(this.status === Commitment.PENDING){
      this.status = Commitment.FULFILLED
      this.result = result
    }
  }
  reject(result){
    if(this.status === Commitment.PENDING){
      this.status = Commitment.REJECTED
      this.result = result
    }
  }
  then(onFULFILLED, onREJECTED){
    return new Commitment((resolve,reject) => {
      onFULFILLED = typeof onFULFILLED === 'function' ? onFULFILLED : () => {}
      onREJECTED = typeof onFULFILLED === 'function' ? onFULFILLED : () => {}
      setTimeout(() => {
        if(this.status === Commitment.FULFILLED){
          // 这里必须调用 resolve 方法包裹,诺不然新的 Promise
          // 会接收不到当前 Promise 的返回值
          // 首先会执行onFULFILLED,也就是then中传来的方法,然后用此方法的返回值传个resolve方法进行更新
          resolve(onFULFILLED(this.result))
        }
        if(this.status === Commitment.REJECTED){
          reject(onREJECTED(this.result))
        }
      });
    })
  }
}
const promise = new Commitment((resolve,reject) => {
  setTimeout(() => {
    resolve("手写Promise")
  });
})
promise.then((result) => {
  console.log("第一个then::", result)
  return 'return给第二个then'
}).then(res =>{
  console.log('第二个then:',res)
}).then(res =>{
  console.log('第三个then:',res)
})

调试的log

console.log('第1步')
const promise = new Commitment((resolve,reject) => {
  console.log('第2步')
  setTimeout(() => {
    resolve("手写Promise")
  });
})
console.log('第3步')
promise.then((result) => {
  console.log("第一个then::", result)
  return 'return给第二个then'
}).then(res =>{
  console.log('第二个then:',res)
}).then(res =>{
  console.log('第三个then:',res)
})
console.log('第4步')
// 第1步
// 第2步
// 第3步
// 第4步
// 第一个then:: 手写Promise
// 第二个then: return给第二个then
// 第三个then: undefined

Promise.resolve()

Promise.resolve = (value => {
  return new Promise((resolve, reject) => {
    if (value instanceof Promise) {
      value.then((res) => {
        resolve(res)
      }, e => {
        reject(e)
      })
    } else {
      resolve(value)
    }
  })
})

Promise.reject()

Promise.reject = (value => {
  return new Promise((resolve, reject) => {
    if (value instanceof Promise) {
      value.then((res) => {
        resolve(res)
      }, e => {
        reject(e)
      })
    } else {
      reject(value)
    }
  })
})

Promise.all()

all(promiseList){
  return new Promise((resolve, reject) => {
    let count = 0
    let returnArr = []
    for (let i = 0; i < promiseList.length; i++) {
      promiseList[i].then(res => {
        count++;
        returnArr.push(res)
        if (count === promiseList.length) {
          resolve(returnArr)
        }
      }, (err => {
        reject(err)
      }))
    }
  })
}

Promise.race()

race(promiseList){
  return new Promise((resolve, reject) => {
   for (let i = 0; i < promiseList.length; i++) {
      promiseList[i].then(res => {
        resolve(res)
      }, (err => {
        reject(err)
      }))
    }
  })
}



目录
相关文章
|
6月前
|
前端开发 JavaScript 安全
Promise/A+ 规范详解:打造健壮异步代码的必备知识(上)
Promise/A+ 规范详解:打造健壮异步代码的必备知识(上)
Promise/A+ 规范详解:打造健壮异步代码的必备知识(上)
|
4月前
|
前端开发 JavaScript
js 等待接口访问成功后执行指定代码【3种方法】(含async await Promise的使用)
js 等待接口访问成功后执行指定代码【3种方法】(含async await Promise的使用)
194 1
|
4月前
|
前端开发 JavaScript 定位技术
JavaScript 等待异步请求数据返回值后,继续执行代码 —— async await Promise的使用方法
JavaScript 等待异步请求数据返回值后,继续执行代码 —— async await Promise的使用方法
54 1
|
6月前
|
前端开发 JavaScript
跟着Promise的节奏,让你的代码脱颖而出
跟着Promise的节奏,让你的代码脱颖而出
|
6月前
|
前端开发 安全
Promise/A+ 规范详解:打造健壮异步代码的必备知识(下)
Promise/A+ 规范详解:打造健壮异步代码的必备知识(下)
Promise/A+ 规范详解:打造健壮异步代码的必备知识(下)
|
前端开发 JavaScript API
重学前端 17 # Promise里的代码为什么比setTimeout先执行?
重学前端 17 # Promise里的代码为什么比setTimeout先执行?
111 0
重学前端 17 # Promise里的代码为什么比setTimeout先执行?
|
前端开发
使用Promise过程中resolve或reject后,后面代码还会执行
在日常开发中会用到Promise语法,里面有resolve和reject
2490 0
|
设计模式 前端开发 JavaScript
图解JavaScript——代码实现【2】(重点是Promise、Async、发布/订阅原理实现)
图解JavaScript——代码实现【2】(重点是Promise、Async、发布/订阅原理实现)
图解JavaScript——代码实现【2】(重点是Promise、Async、发布/订阅原理实现)
|
Web App开发 JavaScript 前端开发
前端通信:ajax设计方案(五)--- 集成promise规范,更优雅的书写代码
  距离上一篇博客书写,又过去了大概几个月了,这段时间暂时离开了这个行业,让大脑休息一下。一个人旅行,一个人休息,正好也去完成一个目标 --- 拥有自己的驾照。当然,也把自己晒的黑漆马虎的。不过这一段时间虽然在技术上没有学太多东西,但是在心态上给了自己一个沉淀的机会,感觉自己变得更加沉稳和成熟,感觉这就是自己需要找到的自己,回归自我。
1450 0
|
Web App开发 JavaScript 前端开发
JavaScript进阶之路——认识和使用Promise,重构你的Js代码
  一转眼,这2015年上半年就过去了,差不多一个月没有写博客了,"罪过罪过"啊~~。进入了七月份,也就意味着我们上半年苦逼的单身生活结束了,从此刻起,我们要打起十二分的精神,开始下半年的单身生活。大家一起加油~~   一直以来,JavaScript处理异步都是以callback的方式,在前端开发领域callback机制几乎深入人心。
1148 0