手写Promise A+

166 阅读10分钟

1. 什么是Promise?

  1. Promise 就是一个类 在执行这个类的时候 需要传递一个执行器进去 执行器会立即执行
  2. Promise 中有三种状态 分别为 成功 fulfilled 失败 rejected 等待 pending pending -> fulfilled pending -> rejected 一旦状态确定就不可更改
  3. resolve和reject函数是用来更改状态的 resolve: fulfilled reject: rejected
  4. then方法内部做的事情就判断状态 如果状态是成功 调用成功的回调函数 如果状态是失败 调用失败回调函数 then方法是被定义在原型对象中的
  5. then成功回调有一个参数 表示成功之后的值 then失败回调有一个参数 表示失败后的原因
  6. 同一个promise对象下面的then方法是可以被调用多次的
  7. then方法是可以被链式调用的, 后面then方法的回调函数拿到值的是上一个then方法的回调函数的返回值

2.下面来看几道题目,通过手写promise规范,可以从中得到答案

题一 题二

console.log('script start')
async function async1() {
    await async2()
    console.log('async1 end')
}
async function async2() {console.log('async2 end')}
async1()
setTimeout(function () {console.log('setTimeout')}, 0)
new Promise(resolve => {
    console.log('Promise')
    resolve()
}).then(function () {
        console.log('promise1')
    }).then(function () {
        console.log('promise2')
    })
console.log('script end')

// 结果如下
// script start
// async2 end
// Promise
// script end
// async1 end
// promise1
// promise2
// setTimeout

作者:小黎也
链接:https://juejin.cn/post/6844904180507738119
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

手写promise

一、准备工作

本次手写使用nodemon代替浏览器运行在node端。

  • 处理化项目,yarn init -y
  • 安装nodemon ,yarn add nodemon -D
  • 新建index.js作为测试用例
  • 创建myPromise.js 作为promise类

二、完成promise类,创建resolve和reject状态器,以及then回调方法(异步)

index.js

/*
  1. Promise 就是一个类 在执行这个类的时候 需要传递一个执行器进去 执行器会立即执行
  2. Promise 中有三种状态 分别为 成功 fulfilled 失败 rejected 等待 pending
    pending -> fulfilled
    pending -> rejected
    一旦状态确定就不可更改
  3. resolve和reject函数是用来更改状态的
    resolve: fulfilled
    reject: rejected
  4. then方法内部做的事情就判断状态 如果状态是成功 调用成功的回调函数 如果状态是失败 调用失败回调函数 then方法是被定义在原型对象中的
  5. then成功回调有一个参数 表示成功之后的值 then失败回调有一个参数 表示失败后的原因
  6. 同一个promise对象下面的then方法是可以被调用多次的
  7. then方法是可以被链式调用的, 后面then方法的回调函数拿到值的是上一个then方法的回调函数的返回值
*/

const MyPromise = require('./wangchunTest');

let promise=new MyPromise((resolve,reject)=>{
  setTimeout(()=>{
    resolve('基本使用')
  },2222)
})

promise.then(value=>{
  console.log(value)
},reason=>{
  console.log(reason)
})

myPromise

// 静态存储

const FULFILLED="fulfilled"
const REJECTED="rejected"
const PENDING = 'pending'; // 等待

class MyPromise {
  constructor (executor){
      executor(this.resolve,this.reject)
  }
  // 初始为等待状态
  status=PENDING
  // 成功之后的值
  value=undefined
  // 失败之后的原因
  reason=undefined
  // 成功回调,改为数组,方便后续链式调用
  successCallback=[]
  // 失败回调,改为数组,方便后续链式调用
  failCallback=[]

  resolve=value=>{
    if (this.status !== PENDING) return
    this.status=FULFILLED
    this.value=value
    this.successCallback&&this.successCallback(value)
  }
  reject=reason=>{
    if (this.status !== PENDING) return
    this.status=REJECTED
    this.reason=reason
    this.failCallback&&this.failCallback(reason)
  }
  then=(successCallback,failCallback)=>{
    if (this.status===FULFILLED){
      successCallback(this.value)
    }else if (this.status===REJECTED){
      failCallback(this.reason)
    }else{
      // 等待状态下,存储两个回调,等待异步结束后调用
      this.successCallback=successCallback
      this.failCallback=failCallback
    }
  }
}
module.exports = MyPromise;
//

**- resolve和reject作为类实例化时传入的方法,执行哪个就更改对应的状态。

  • 同时在then中执行对应的回调
  • 当两个修改状态的方法中含有异步代码时候,then逻辑判断进入else,也就是等待状态
  • 存储回调,等异步代码结束后,执行**

三、完成then的链式调用

// 静态存储

const FULFILLED="fulfilled"
const REJECTED="rejected"
const PENDING = 'pending'; // 等待

class MyPromise {
  constructor (executor){
    executor(this.resolve,this.reject)
  }
  // 初始为等待状态
  status=PENDING
  // 成功之后的值
  value=undefined
  // 失败之后的原因
  reason=undefined
  // 成功回调,改为数组,方便后续链式调用
  successCallback=[]
  // 失败回调,改为数组,方便后续链式调用
  failCallback=[]

  resolve=value=>{
    // 修改为成功或者失败后不可进行变更
    if (this.status !== PENDING) return
    // 修改状态为成功
    this.status=FULFILLED
    // 保存成功之后的值
    this.value=value
    // 判断成功回调队列长度是否为0 ,如果出栈并调用
    while (this.successCallback.length) this.successCallback.shift()(this.value)
  }
  reject=reason=>{
    // 如果状态不是等待 阻止程序向下执行
    if (this.status !== PENDING) return
    // 将状态更改为失败
    this.status=REJECTED
    // 保存失败后的原因
    this.reason=reason
    // 判断失败回调队列长度是否为0 ,如果出栈并调用
    while (this.failCallback.length) this.failCallback.shift()(this.reason)
  }
  then=(successCallback,failCallback)=>{
    // 需要链式回调就需要返回promise对象,首先创建promise
    let nextPromise=new MyPromise((resolve,reject)=>{
      if (this.status===FULFILLED){
        // 存储本次成功回调的值
        let x=successCallback(this.value)
        // 作为参数返回给下一个promise实例的resolve
        resolve(x)
      }else if (this.status===REJECTED){
        failCallback(this.reason)
      }else{
        // 等待状态下,存储两个回调,等待异步结束后调用
        this.successCallback.push(successCallback)
        this.failCallback.push(failCallback)
      }
    })
    return nextPromise
  }
}
module.exports = MyPromise;

链式调用的前提是,上一个then返回实例对象,故此处,在then中实例化promise,并且返回

四、添加错误捕获、自调用错误、then可选参数

index

let promise=new MyPromise((resolve,reject)=>{
  // reject('失败')
  setTimeout(()=>{
    reject('das111d')
  },2222)
})
promise.then().then().then(value=>console.log(value),reason=>console.log(reason))

myPromise

// 静态存储

const FULFILLED="fulfilled"
const REJECTED="rejected"
const PENDING = 'pending'; // 等待

class MyPromise {
  constructor (executor){
    try {
      executor(this.resolve,this.reject)
    }catch (e) {
      // 捕获错误
      this.reject(e)
    }
  }
  // 初始为等待状态
  status=PENDING
  // 成功之后的值
  value=undefined
  // 失败之后的原因
  reason=undefined
  // 成功回调,改为数组,方便后续链式调用
  successCallback=[]
  // 失败回调,改为数组,方便后续链式调用
  failCallback=[]

  resolve=value=>{
    // 修改为成功或者失败后不可进行变更
    if (this.status !== PENDING) return
    // 修改状态为成功
    this.status=FULFILLED
    // 保存成功之后的值
    this.value=value
    // 判断成功回调队列长度是否为0 ,如果出栈并调用
    while (this.successCallback.length) this.successCallback.shift()()
  }
  reject = reason => {
    // 如果状态不是等待 阻止程序向下执行
    if (this.status !== PENDING) return;
    // 将状态更改为失败
    this.status = REJECTED;
    // 保存失败后的原因
    this.reason = reason;
    // 判断失败回调是否存在 如果存在 调用
    // this.failCallback && this.failCallback(this.reason);
    while (this.failCallback.length) this.failCallback.shift()()
  }
  then(successCallback,failCallback){
    // 参数可选
    successCallback = successCallback ? successCallback : value => value;
    // 参数可选
    failCallback = failCallback ? failCallback : reason => {
      throw reason
    };
    // 需要链式回调就需要返回promise对象,首先创建promise
    let nextPromise=new MyPromise((resolve,reject)=>{
      if (this.status===FULFILLED){
        setTimeout(()=>{
          try {
            // 存储本次成功回调的值
            let x=successCallback(this.value)
            // 判断 x 的值是普通值还是promise对象
            // 如果是普通值 直接调用resolve
            // 如果是promise对象 查看promsie对象返回的结果
            // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
            // 因为then方法是需要实例生成后才能调用的,所以此时拿不到实例队形,需要通过异步函数,放到栈底执行
            resolvePromise(nextPromise,x,resolve,reject)
          }catch (e) {
            reject (e)
          }
        },0)
      }else if (this.status===REJECTED){
        setTimeout(() => {
          try {
            let x = failCallback(this.reason);
            // 判断 x 的值是普通值还是promise对象
            // 如果是普通值 直接调用resolve
            // 如果是promise对象 查看promsie对象返回的结果
            // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
            resolvePromise(nextPromise, x, resolve, reject)
          } catch (e) {
            reject(e);
          }
        }, 0)
      }else{
        // 等待状态下,存储两个回调,等待异步结束后调用
        this.successCallback.push(()=>{
          setTimeout(()=>{
            try {
              let x=successCallback(this.value)
              resolvePromise(nextPromise,x,resolve,reject)
            }catch (e) {
              reject (e)
            }
          },0)
        });

        this.failCallback.push(()=>{
          setTimeout(()=>{
            try {
              let x=failCallback(this.reason)
              resolvePromise(nextPromise,x,resolve,reject)
            }catch (e) {
              reject (e)
            }
          },0)
        })
      }
    })
    return nextPromise
  }
}
function resolvePromise(promsie2, x, resolve, reject) {
  if (promsie2 === x) {
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  }
  if (x instanceof MyPromise) {
    // promise 对象
    // x.then(value => resolve(value), reason => reject(reason));
    x.then(resolve, reject);
  } else {
    // 普通值
    resolve(x);
  }
}
module.exports = MyPromise;

五、添加静态方法all、resolve,及finaaly、catch方法

index

/*
  1. Promise 就是一个类 在执行这个类的时候 需要传递一个执行器进去 执行器会立即执行
  2. Promise 中有三种状态 分别为 成功 fulfilled 失败 rejected 等待 pending
    pending -> fulfilled
    pending -> rejected
    一旦状态确定就不可更改
  3. resolve和reject函数是用来更改状态的
    resolve: fulfilled
    reject: rejected
  4. then方法内部做的事情就判断状态 如果状态是成功 调用成功的回调函数 如果状态是失败 调用失败回调函数 then方法是被定义在原型对象中的
  5. then成功回调有一个参数 表示成功之后的值 then失败回调有一个参数 表示失败后的原因
  6. 同一个promise对象下面的then方法是可以被调用多次的
  7. then方法是可以被链式调用的, 后面then方法的回调函数拿到值的是上一个then方法的回调函数的返回值
*/

const MyPromise = require('./wangchunTest');

function p1 () {
  return new MyPromise(function (resolve, reject) {
    setTimeout(function () {
      reject('p1')
    }, 2000)
  })
}
function p2 () {
  return new MyPromise(function (resolve, reject) {
    resolve('p2')
    // resolve('成功');
  })
}
p2().finally(()=>{
  console.log('finally');
  return p1()
}).then(value=>{
  console.log(value);
},reason=>{
  console.log(reason);
})

myPromise

// 静态存储

const FULFILLED = "fulfilled"
const REJECTED = "rejected"
const PENDING = 'pending'; // 等待

class MyPromise {
  constructor(executor) {
    try {
      executor(this.resolve, this.reject)
    } catch (e) {
      // 捕获错误
      this.reject(e)
    }
  }

  // 初始为等待状态
  status = PENDING
  // 成功之后的值
  value = undefined
  // 失败之后的原因
  reason = undefined
  // 成功回调,改为数组,方便后续链式调用
  successCallback = []
  // 失败回调,改为数组,方便后续链式调用
  failCallback = []

  resolve = value => {
    // 修改为成功或者失败后不可进行变更
    if (this.status !== PENDING) return
    // 修改状态为成功
    this.status = FULFILLED
    // 保存成功之后的值
    this.value = value
    // 判断成功回调队列长度是否为0 ,如果出栈并调用
    while (this.successCallback.length) this.successCallback.shift()()
  }
  reject = reason => {
    // 如果状态不是等待 阻止程序向下执行
    if (this.status !== PENDING) return;
    // 将状态更改为失败
    this.status = REJECTED;
    // 保存失败后的原因
    this.reason = reason;
    // 判断失败回调是否存在 如果存在 调用
    // this.failCallback && this.failCallback(this.reason);
    while (this.failCallback.length) this.failCallback.shift()()
  }

  then(successCallback, failCallback) {
    // 参数可选
    successCallback = successCallback ? successCallback : value => value;
    // 参数可选
    failCallback = failCallback ? failCallback : reason => {
      throw reason
    };
    // 需要链式回调就需要返回promise对象,首先创建promise
    let nextPromise = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        setTimeout(() => {
          try {
            // 存储本次成功回调的值
            let x = successCallback(this.value)
            // 判断 x 的值是普通值还是promise对象
            // 如果是普通值 直接调用resolve
            // 如果是promise对象 查看promsie对象返回的结果
            // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
            // 因为then方法是需要实例生成后才能调用的,所以此时拿不到实例队形,需要通过异步函数,放到栈底执行
            resolvePromise(nextPromise, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        }, 0)
      } else if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let x = failCallback(this.reason);
            // 判断 x 的值是普通值还是promise对象
            // 如果是普通值 直接调用resolve
            // 如果是promise对象 查看promsie对象返回的结果
            // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
            resolvePromise(nextPromise, x, resolve, reject)
          } catch (e) {
            reject(e);
          }
        }, 0)
      } else {
        // 等待状态下,存储两个回调,等待异步结束后调用
        this.successCallback.push(() => {
          setTimeout(() => {
            try {
              let x = successCallback(this.value)
              resolvePromise(nextPromise, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          }, 0)
        });

        this.failCallback.push(() => {
          setTimeout(() => {
            try {
              let x = failCallback(this.reason)
              resolvePromise(nextPromise, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          }, 0)
        })
      }
    })
    return nextPromise
  }

  finally(callback) {
    return this.then(value => {
      return MyPromise.resolve(callback()).then(() => value)
    }, reason => {
      return MyPromise.resolve(callback()).then(() => {
        throw reason
      })
    })
  }
  catch (failCallback){
    return this.then(undefined,failCallback)
  }
  
  static resolve(value) {
    if (value instanceof MyPromise) return value
    return new MyPromise(resolve => resolve(value))
  }
  // 无需被实例化即可调用,切多个实例化队形都可调用的需要作为静态方法
  static all(array) {
    // 结果数组
    let result = []
    // 标记成功添加计步
    let index = 0
    return new MyPromise((resolve, reject) => {
      function addData(key, value) {
        result[key] = value;
        index++;
        // 全部完成后返回,防止异步问题
        if (index === array.length) {
          resolve(result)
        }
      }

      for (let i = 0; i < array.length; i++) {
        let current = array[i]
        if (current instanceof MyPromise) {
          // 如果是promise对象
          current.then(value => addData(i, value), reason => reject(reason))
        } else {
          // 如果仅仅是普通值
          addData(i, array[i])
        }

      }
    })
  }
}

// 所有promise对象都需要使用的方法,应该用静态方法


function resolvePromise(promsie2, x, resolve, reject) {
  if (promsie2 === x) {
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  }
  if (x instanceof MyPromise) {
    // promise 对象
    // x.then(value => resolve(value), reason => reject(reason));
    x.then(resolve, reject);
  } else {
    // 普通值
    resolve(x);
  }
}

module.exports = MyPromise;

本文结,通过上述五个步骤,可以了解到promise A+规范的手写流程,同时也可以深入了解到promise的执行时序。