使用ES6中Class实现手写PromiseA+,完美通过官方872条用例





!

🧡最近忙着事业,又忙着晋升学习,还要搬家,好久没有输出了,不过还是要抽出边边角角的时间,分享一些好的内容,如果对你有所帮助,笔个芯再走吧🧡
废话不多说,相信大多数人,一提到Promsie就能条件反射想到回调地狱,看了ES6官方晦涩难懂的解释,感觉一脸懵逼,害怕面试的时候被问到,答起来完全不知道自己在说什么,那么今天就来帮你解决掉这一大难题。

长篇预警!有点长,可以选择性观看。本篇主要带领你手写一个Promise源码,学完你就会发现:Promise没有你想象中的那么难,如果对Promise源码不是很清楚,还是推荐从头看,相信你认真从头看到尾,并且去实际操作了,肯定会有收获的。主要是代码部分有点多,不过好多都是重复的,不必担心

Promise出现的原因

promise出现的原因,说白了也就是promise解决了什么问题,这里我就简单说点吧:
Promise 是异步编程的一种解决方案,比传统的解决方案回调函数事件更合理和更强大,Promise的出现主要解决以下两个问题:

  1. 回调地狱: 某个异步操作需要等待之前的操作完成在继续执行, 当这样的需求多了以后, 使的代码进入无尽的嵌套,可读性降低不好维护
  2. 异步和同步之间的联系问题:当一个同步操作需要等待多个异步操作的结果, 这样会使得代码的逻辑变得相对来说比较复杂

myPromise的实现要点

  1. Promise 就是一个,实际上就是ES6提供的一个新的构造函数
  2. 通过new创建实例,接收一个函数作为参数,并且该函数中的代码默认是同步代码,会立即执行
  3. Promise 有三种状态,成功resolved失败rejected等待pedding
  4. Promise 状态一旦发生改变,就不能在改变,状态不可逆
  5. 用户可自定义成功的数据失败的原因
  6. Promise 实例拥有一个then方法。接受两个参数,一个成功回调,一个失败回调
  7. executor执行器在执行过程中,可能会抛出异常 throw new Error(),需要 try catch 捕获
  8. 当执行器中的是异步代码时(如定时器执行resolve),不是立即执行 状态不会变更,此时then方法中状态仍然是pending
  9. 一个 promise 实例可以被 then 多次,分别创建一个队列用来存放成功和失败回调事件
  10. 定时器执行时,判断状态为 pending,非立即执行,而是先存放 成功/失败 回调事件
  11. 等待状态变为成功或失败时遍历队列依次执行对应的onfulfilledonrejected,并且有执行顺序
  12. Promise 实现链式调用,返回的并不是this,而是一个新的promise实例, 因为原有的promise状态一旦发生改变,就不能再改变,否则不符合规范
  13. Promise 成功和失败的回调的返回值,可以传递给下一次的的then
  • 13.1. 返回的是普通值:不论then是成功还是失败。只要是普通值就传递到下一次then的成功中。(普通值包括:非错误非promise,包括对象)
  • 13.2. 报错或异常:一定会走到下一次then的失败中。如果离自己最近的then没有错误处理,会向下找
    • 特别注意: return new Error()返回的是错误对象,属于普通值走下一次then的成功,而throw new Error() 是抛出异常,需要try catch 会走下一次的失败
  • 13.3. 返回的是promise:会采用promise的状态,决定下一次then是成功还是失败,此处会有递归判断
  • 13.4. 总结: 如果返回一个普通值,除了promise ,就传递给下一个then的成功,如果返回一个失败的promise或者抛出异常,会走下一个then的失败
  1. Promise 值的穿透,当一个promise连续then 多次,并且then中没有返回任何值时,此时data会穿透至最后一个then中
    • 14.1 执行onfulfilled时,先判断是不是一个function,如果是就直接执行,反之就包一层函数
    • 14.2 执行onrejected时,先判断是不是一个function,如果是就直接执行,反之就抛出失败异常
  2. 既然promise是为了解决异步回调函数嵌套问题,那么可以通过promise的延迟对象来减少一层嵌套关系
  3. promiseA+ 规范测试
  4. 聊点规范以外的东东吧~~catch, finally,resolve,reject,all,race,就这么多

myPromise的实现

下面就来实现以上要点

myPromise — 实现简单的同步

myPromise.js

// 要点 3: 有三种状态, 相当于是常量,可以放在外面
const RESOLVED = 'RESOLVED'
const REJECTED = 'REJECTED'
const PENDING = 'PENDING'
// 要点 1: Promise 就是一个类
class Promise {
  // 要点 2: 接收一个函数executor作为参数,立即执行
  constructor(executor) {
    this.status = PENDING
    this.value = undefined  // 要点 5: 用户可自定义 成功的数据
    this.reason = undefined // 要点 5: 用户可自定义 失败的原因
    let resolve = (value) => {
      // 要点 4: 状态不可逆, 只有PENDING时,才可以改变状态
      if (this.status === PENDING) {
        this.value = value       // 成功的数据
        this.status = RESOLVED   // 状态置为 RESOLVED
      }
    }
    let reject = (reason) => {
      // 要点 4: 状态不可逆, 只有PENDING时,才可以改变状态
      if (this.status === PENDING) {
        this.reason = reason     // 失败的原因
        this.status = REJECTED   // 状态置为 REJECTED
      }
    }

    // 要点 7: 错误处理,抛出异常 throw new Error()
    try {  
      executor(resolve, reject) // 立即执行
    } catch(e) {   // 抛出异常 直接失败
      reject(e)
    }
  }
  // 要点 6: 拥有一个then方法(实例上的方法)。接受两个参数回调函数
  then(onfulfilled, onrejected) {
    if(this.status === RESOLVED){
      onfulfilled(this.value)
    }
    if(this.status === REJECTED){
      onrejected(this.reason)
    }
  }
}
// Commonjs规范导出模块
module.exports = Promise

test.js:简单的同步操作验证

// 使用自己的promise,注释该行代码,就是原生promise
let Promise = require('./1.myPromise')

let p = new Promise((resolve, reject) => {
  resolve('成功')
  // throw new Error('失败了')
  // reject('失败')
})

p.then((data) => {
  console.log(data)
}, (err) => {
  console.log(err)
})

myPromise — 增加异步功能

在实现了简单的同步操作后,再来看看异步代码的执行,还是使用上面 test 中的案例,简单的改造一下,写一个定时器模拟异步环境,

test.js:增加异步功能

let Promise = require('./1.myPromise')

let p = new Promise((resolve, reject) => {
  setTimeout(()=>{
      resolve('成功')
  },1000)
})
  
p.then((data) => {
   console.log(data,1)
}, (err) => {
   console.log(err)
})
// 一个实例可以then多次,执行结果会是一样的,因为状态已经固定
p.then((data) => {
   console.log(data,2)
}, (err) => {
   console.log(err)
})

myPromise.js

const RESOLVED = 'RESOLVED'
const REJECTED = 'REJECTED'
const PENDING = 'PENDING'
class Promise {
  constructor(executor) {
    this.status = PENDING
    this.value = undefined   
    this.reason = undefined 
    // 要点 9: 分别创建一个`队列用来存放成功和失败回调事件
    this.onfulfilledCallbacks = []
    this.onrejectedCallbacks = []
    let resolve = (value) => {
      if (this.status === PENDING) {
        this.value = value
        this.status = RESOLVED
        this.onfulfilledCallbacks.forEach(fn => fn())  // 要点 11: 状态变为成功时遍历队列依次执行
      }
    }
    let reject = (reason) => {
      if (this.status === PENDING) {
        this.reason = reason
        this.status = REJECTED
        this.onrejectedCallbacks.forEach(fn => fn())   // 要点 11: 状态变为失败时遍历队列依次执行
      }
    }

    try {  
      executor(resolve, reject) 
    } catch(e) { 
      reject(e)
    }
  }
  then(onfulfilled, onrejected) {
    if(this.status === RESOLVED){
      onfulfilled(this.value)
    }
    if(this.status === REJECTED){
      onrejected(this.reason)
    }
    // console.log(this.status)   // PENDING
    // 要点 8: 定时器执行resolve时,状态仍然是pending
    if(this.status === PENDING){
      // 要点 10: 一个 promise 实例可以被 then 多次,存放 成功回调事件
      this.onfulfilledCallbacks.push(() => {
        onfulfilled(this.value)
      })
      // 要点 10: 一个 promise 实例可以被 then 多次,存放 失败回调事件
      this.onrejectedCallbacks.push(() => {
        onrejected(this.reason)
      })
    }
  }
}
// Commonjs规范导出模块
module.exports = Promise

myPromise — 链式调用(重难点,不好理解)

链式调用这一部分,比较绕,不太好理解,我的老师鼓励我,说:“书读百遍,其义自现”,哈哈哈,现在用来鼓励大家吧!!!
test.js

let Promise = require('./1.myPromise')

let p = new Promise((resolve, reject) => {
  resolve('成功')
})

// 可以分别注释用例代码,进行效果演示
p.then((data) => {
  return data              // 用例1. 返回的是普通值: ‘成功’
  // throw new Error()     // 用例2. 报错或异常: 抛出异常,会直接走下一次then的失败
  // return new Error()    // 用例3. 返回的是普通值: error对象,需要特别注意
  // return new Promise((s,j)=>s(1))    // 用例4. 返回的是promise: 会传递当前promise的已成功结果
  // return new Promise((s,j)=>j(1))    // 用例5. 返回的是promise: 会传递当前promise的已失败结果
}).then((data) => {
  console.log(data,2)      // 执行结果:用例1, 用例3, 用例4 
}, (err) => {
  console.log(err,3)       // 执行结果:用例2, 用例5
})

// 1. 返回的是普通值:不论then是成功还是失败。只要是普通值就传递到下一次then的成功中。(普通值包括:非错误非promise,包括对象)
// 2. 报错或异常:一定会走到下一次then的失败中。如果离自己最近的then没有错误处理,会向下找
// 3. 特别注意: return new Error()返回的是错误对象,属于普通值走下一次then的成功,而throw new Error() 是抛出异常,会走下一次的失败
// 4. 返回的是promise:会采用promise的状态,决定下一次then是成功还是失败,此处会有递归判断
// 5. 总结: 如果返回一个普通值,除了promise ,就传递给下一个then的成功,如果返回一个失败的promise或者抛出异常(try catch),会走下一个then的失败

myPromise.js

const RESOLVED = 'RESOLVED'
const REJECTED = 'REJECTED'
const PENDING = 'PENDING'
const resolvePromise = (promise2, x, resolve, reject) => {
  if (promise2 === x) { // 防止自己等待自己 一直循环等待 
    // 原生返回:return reject(new TypeError('TypeError: Chaining cycle detected for promise #<Promise>'))
    return reject(new TypeError('我们自己报的错:循环引用报错'))
  }
  let called; // 为了兼容其他promise 库符合a+规范,防止状态改变后再次被调用
  // 考虑其他promise库兼容问题:判断x是不是promise: 不是null的对象  || 函数  
  if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
    try { // 为防止then时 出现异常,Object.defineProperty
      let then = x.then // 取x 的then 方法 
      /**
       * 可能对象是{then:{}},还需要判断then是否是函数,
       * 是函数就认为是promise,并调用then方法
       */
      if (typeof then === 'function') {
        // call第一个参数是this,后面的是成功回调 和 失败回调
        // 如果成功回调返回值 y 仍然是promise,就继续递归解析promise,解析到返回值不是一个Promise类型为止
        // promise2能否成功,是根据x的值来定的,x是promise,那么就要等到x完成,x完成后,如果返回y又是promise,那promise2又要等到y完成
        then.call(x, y =>{
          if (called) return
          called = true
          resolvePromise(promise2, y, resolve, reject)
        }, e => { 
          if (called) return
          called = true
          reject(e) 
        })
      } else {    
        // 否则就认为 then 是一个普通对象,成功即可
        resolve(x)
      }
    } catch (e) {
      if (called) return
      called = true
      reject(e)   // 出现异常,直接走失败逻辑
    }
  } else { // x 为普通值
    resolve(x)
  }
}
class Promise {
  constructor(executor) {
    this.status = PENDING
    this.value = undefined
    this.reason = undefined
    this.onfulfilledCallbacks = []
    this.onrejectedCallbacks = []
    let resolve = (value) => {
      if (this.status === PENDING) {
        this.value = value
        this.status = RESOLVED
        this.onfulfilledCallbacks.forEach(fn => fn())
      }
    }
    let reject = (reason) => {
      if (this.status === PENDING) {
        this.reason = reason
        this.status = REJECTED
        this.onrejectedCallbacks.forEach(fn => fn())
      }
    }
    try {
      executor(resolve, reject)
    } catch (e) {
      reject(e)
    }
  }

  then(onfulfilled, onrejected) {
    // 要点 12: then链式调用,返回的并不是this,而是一个`新的promise实例`
    let promise2 = new Promise((resolve, reject) => { // new Promise执行器中的代码是立即执行,因此没有影响
      if (this.status === RESOLVED) {
        // 加定时器是因为在当前执行上下文中,不能获取promise2的值
        setTimeout(() => { 
          // 要点13.2. 用于报错或抛出异常处理,以下同理
          try { 
            // 要点13:存储第一次then的回调结果返回值,用于下次then的参数传递,以下let x 同理
            let x = onfulfilled(this.value)
            // 要点13.3: x 可能会是一个promise, 需要单独处理,
            // 并将promise2、新的返回值:x 、成功回调resolve  失败回调reject 作为参数传递
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            // 要点13.2. 报错或抛出异常处理,直接走promise2的reject,以下同理
            reject(e) 
          }
        })
      }
      // 以下逻辑同理
      if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let x = onrejected(this.reason)
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        })
      }
      if (this.status === PENDING) {
        // 暂存成功回调队列
        this.onfulfilledCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onfulfilled(this.value)
              resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          })
        })
        // 暂存失败回调队列
        this.onrejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onrejected(this.reason)
              resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          })
        })
      }
    })
    return promise2 // 要点 12: 返回一个新的promise实例
  }
}

// Commonjs规范导出模块
module.exports = Promise

myPromise — 值的透传

Promise 值的穿透,当一个promise连续then多次 ,并且then中没有返回任何值时,此时data会穿透值最后一个then中

test.js

//原生方法:值的穿透 演示
let p = new Promise((resolve,reject)=>{
  resolve(1)
  // reject(2)   // 同理
})
p.then().then().then(data=>{
  console.log(data);   // 1
})
p.then().then().then(null,err=>{
  console.log(err);    // 2
})

// ----------------------------------------------------------

//myPromise:基于以上几部分的实现,我们自己的promise是不支持穿透的,那么看一下演变过程,就很容易写出符合规范的透传
const Promise = require('./1.myPromise')
let p1 = new Promise((resolve,reject)=>{
  resolve(1)
})
let p2 = new Promise((resolve,reject)=>{
  reject(2)   // 同理,
})

// 相当于, 啥都不写时,感觉像是默认定义了一个函数,不停向下传递,去实现一下吧~
p1.then(data => data)
  .then(data => data)
  .then(data =>{
    console.log(data);
  })

// 相当于, throw err,不停向下传递
p2.then(null,err=>{
  throw err
}).then(null,err=>{
  console.log(err);
  throw err 
}).then(null,err=>{
  console.log(err);
})

myPromise.js
上面重复的代码是在是太多了,就不在赘述了,怕看着更晕了,来, 找到类中then方法定义的位置,加上下面两行代码,就可以完美实现成功及失败的数据透传

  ...
  ...
  ...
  // 在这里这里...其他都不变
  then(onfulfilled, onrejected) {
    // onfulfilled 是函数就执行,不是函数包一层函数并直接返回数据
    onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : v => v;
    // onrejected 是函数就执行,不是函数包一层函数并直接抛出错误
    onrejected = typeof onrejected === 'function' ? onrejected : err => { throw err };
  ...
  ...
  ...

完美通过官方872条用例

为了测试myPromise库,必须公开一个非常小的适配器接口。可以说是promise的延迟对象defer,下文会详细介绍,各位稍安勿躁。

  1. 第一步:找到文件最下面。加上下面几行代码,
  /**
   *   ...
   * 其余不变
   *   ...
   */
// 测试自己的写的promise 是否符合a+规范
// promise的延迟对象
Promise.defer = Promise.deferred = function () {
  let dfd = {}
  dfd.promise = new Promise((resolve, reject) => {
    dfd.resolve = resolve;
    dfd.reject = reject
  })
  return dfd;
}

module.exports = Promise
  1. 第二步:安装测试包 npm install promises-aplus-tests -g
  2. 第三步:进入对应目录执行 promises-aplus-tests ./1.myPromise.js
  3. 如果你的测试结果和我图中标注一样,那么恭喜你,如果验证不通过,肯定是有一些不一样,注意看报错信息,修正一下就好了

myPromise的延迟对象defer用法

promise是为了解决异步回调函数嵌套问题,那么可以通过promise的延迟对象来减少一层嵌套关系
模拟数据准备,准备以下两个文件,

  1. name.txt:文件内容是:age.txt
  2. age.txt:文件内容18
    需求:首先通过fs模块读取到name中的内容(读到新的filepath),再去读取age.txt中的内容并输出

没有延迟对象的写法:

const fs = require('fs')
const Promise = require('./1.myPromise')

function read(filepath){
  // 想要read方法可以使用then方法,需要包一层new Promise
  return new Promise((resolve,reject)=>{   
    fs.readFile(filepath,'utf8',function(err,data){
      if(err) return reject(err)
      resolve(data)
    })
  })
}

read('./name.txt').then(data=>{
  return read(data)    // data = age.txt
}).then(data=>{
  console.log(data);   // data = age.txt
})

使用延迟对象的写法:

const fs = require('fs')
const Promise = require('./1.myPromise')

function readDefer(filepath){
  let dfd = Promise.defer()   // 减少一层嵌套
  fs.readFile(filepath,'utf8',function(err,data){
    if(err) return dfd.reject(err)
    dfd.resolve(data)
  })
  return dfd.promise
}

readDefer('./name.txt').then(data=>{
  return readDefer(data) // data = age.txt
}).then(data=>{
  console.log(data);    // data = age.txt
})

myPromise.catch

完成以上内容,就已经实现了Promise A+ 规范的全部内容,而我们常用的catchfinally并不属于 A+规范,同样可以实现一下,catch是属于实例上的方法,用于出错处理及捕获异常
演变过程:test.js

const Promise = require('./1.myPromise')

new Promise((resolve, reject) => {
  reject('失败') 
}).then(data => {         // 第一个then没有错误处理方法,直接会传递到第二个then中
  console.log(data);
}).then(null, err => {
  console.log(err, 'errrrrr');     // 失败 errrrrr
})

// 将上述代码中null优化一下,于是就有了catch方法
new Promise((resolve, reject) => {
  reject('失败') 
}).then(data => {         
  console.log(data);
}).catch(err => {
  console.log(err, 'errrrrr');     // 失败 errrrrr
})

实现:myPromise.js

class Promise{
  // 重复代码省略
  // constructor(){...}
  // then(){...}

  catch(onrejected){    // 实例的catch
    return this.then(null,onrejected)
  }
}
/**
 * 其余重复代码省略
 */

myPromise.finally

finally方法是属于实例上的方法.表示无论成功还是失败,都会执行,并且可以向下执行。理解起来有点点绕哈,建议多看几遍吧,
test.js

const Promise = require('./1.myPromise')

let p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    // resolve('成功')  // 1
    reject('失败')      // 2
  }, 1000)
}).finally(() => { 
  return new Promise((resolve, reject) => { 
    setTimeout(() => {
      // resolve(1000)  // 3 不会采用promise成功的结果,但是会等待执行
      reject(9000)      // 4 但是返回一个失败的promise,会走catch,相当于throw new Error
    }, 1000)
  })
}).then(data => {
  console.log(data);      // 对应上述不同序号组合,输出结果有所不同,可自行组合实验,下方也会给大家作出总结
}).catch(e => {
  console.log(e, 'catch');
})

/**
 * 整理流程:
 * 组合:
 * 1+3:成功 + 成功 ,走then=>data 返回 1 返回的数据('成功')
 * 1+4:成功 + 失败 ,走then=>err 抛出 4 返回的数据(9000)
 * 2+3:失败 + 成功 ,走then=>err 抛出 2 返回的数据('失败')
 * 2+4:失败 + 失败 ,走then=>err 抛出 4 返回的数据(9000)
 */

实现:myPromise.js

/**
 * 1. finally 传递了一个方法 callback
 * 2. finally 方法相当于一个then,特点就是在函数中返回一个promise,并会等待这个promise的完成
 * 3. Promise.resolve 又会等待 callback执行完成
 */

class Promise{
  // 重复代码省略
  // constructor(){...}
  // then(){...}
  // catch(){...}
  finally(cb){     
    return this.then((data)=>{   // 成功 || 失败 都会执行
      // Promise.resolve 目的是等待cb() 后的promise完成
      // 成功时调用finally,直接传递this实例成功的数据data,finally方法本身是没有参数的
      return Promise.resolve(cb()).then(()=>data)  
    },(err)=>{
      // 失败时调用finally,等待Promise.resolve(cb())的执行的结果,当cb结果为失败时,直接以失败的结果直接走catch
      // 而只有在Promise.resolve成功时候,才会去执行.then(()=>{throw err}),抛出this实例reject的err信息
      return Promise.resolve(cb()).then(()=>{throw err})
    })
  }
}
/**
 * 其余重复代码省略
 */

myPromise.resolve

Promise.resolve() 方法会创造一个成功的promsie,属于类上的方法,通过className调用
演变过程:test.js

const Promise = require('./1.myPromise')

Promise.resolve(200).then(data => {
  console.log(data);     // 200
})

// 等价于
new Promise((resolve, reject) => {
  resolve(200)
}).then(data => {
  console.log(data);     // 200
})

实现:myPromise.js

constructor(executor) {
  /**
   * 其余重复无改动
   */
  let resolve = (value) => {
    // 一个promise直接resolve 另一个promise时,会等待里面的promise执行完,返回成功的执行结果
    if (value instanceof Promise) {
      return value.then(resolve, reject)
    }
    if (this.status === PENDING) {
      this.value = value
      this.status = RESOLVED
      this.onfulfilledCallbacks.forEach(fn => fn())
    }
  }
  let reject = (reason) => {
    // reject 一个promise时,不需要单独处理,失败就直接走失败的逻辑,不用处理其返回值
    if (this.status === PENDING) {
      this.reason = reason
      this.status = REJECTED
      this.onrejectedCallbacks.forEach(fn => fn())
    }
  }
  /**
   * 其余重复无改动
   */
}
static resolve(value) {
  return new Promise((resolve, reject) => {
    resolve(value)
  })
}

myPromise.reject

Promise.reject() 方法会创造一个失败的promsie,属于类上的方法,通过className调用

演变过程:test.js

const Promise = require('./1.myPromise')

Promise.reject(100).then(null,err => {
  console.log(err, 'errrrrr');   // 100 errrrrr
})

// 等价于
new Promise((resolve, reject) => {
  reject(100)
}).then(data=>{
  console.log(data);     
},err => {
  console.log(err, 'errrrrr');    // 100 errrrrr
})

实现:myPromise.js

class Promise{
  /**
   * 其余重复无改动
   */
  static reject(reason) {
    return new Promise((resolve, reject) => {
      reject(reason)
    })
  }
}
/**
 * 其余重复无改动
 */

myPromise.all

Promise.all()方法用于将多个 Promise 实例,包装并返回一个新的 Promise 实例p

p的状态由传递的多个 Promise 决定,分成两种情况。(官网是这样说的)

(1)只有所有promise实例的状态都变成fulfilledp的状态才会变成fulfilled,此时数组中promise的返回值组成一个数组,传递给p的回调函数。

(2)只要数组的中promise有一个被rejectedp的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
test.js

const Promise = require('./1.myPromise')

Promise.all([
  1, 2, 3, 
  new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('成功')
    }, 1000)
  }), 
  new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('成功')    // 情况1
      // reject('失败')  // 情况2
    }, 1000)})
  ]).then(data => {
    console.log(data);   // 情况1. [ 1, 2, 3, '成功', '成功' ]
  }).catch(err => {
    console.log(err);    // 情况2. 失败
  })

myPromise.js

class Promise{
  // 在原有代码基础上,添加一个静态方法
  static all(promises){
    return new Promise((resolve,reject)=>{
      let result = []
      let times = 0
      const processSuccess = (index,val)=>{
        result[index] = val
        if(++times === promises.length){
          resolve(result)
        }
      }
      for (let i = 0; i < promises.length; i++) { // 并发。多个请求一起执行
        let p = promises[i];
        if(p && typeof p.then === 'function'){   // 判断p是不是promise实例,是则继续等待p的执行结果,处理p对应的成功和失败
          p.then(data=>{
            processSuccess(i,data)
          },reject) // 如果其中一个promise失败了,直接执行失败回调,由于reject本身就是函数,故可简写
        }else{   // 如果不是promise实例就直接调用成功,并存储该值
          processSuccess(i,p)
        }
      }
    })
  }
}

myPromise.race

Promise.race()方法同样是将多个 Promise 实例,包装并返回一个新的 Promise 实例p
只要有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。
(简单理解:race 赛跑,采用最快的那一个,race方法如果其中一个完成了,其他方法还是会执行完,只是并没有采用他的结果)
test.js

const Promise = require('./1.myPromise')
Promise.race([ 
  new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('成功')
    }, 2000)
  }), 
  new Promise((resolve, reject) => {
    setTimeout(() => {
      reject('失败')
    }, 1000)
  })
]).then(data => {
  console.log(data);
}).catch(err => {
  console.log(err);   // 输出:失败,resolve 2s执行,reject 1s执行,reject更快
})

myPromise.js

class Promise{
  static race(promises){
    return new Promise((resolve,reject)=>{
      // for循环执行,只要有一个状态发生改变,就直接将该实例的返回值 返回
      for (let i = 0; i < promises.length; i++) {
        let p = promises[i];
        if(p && typeof p.then === 'function'){
          p.then(data=>{
            resolve(data)
          },reject)
        }else{
          resolve(p)
        }
      }
    })
  }
}  

myPromise终极版本

😂😂在我耐心终将耗尽之际,终于迎来了终极版
show一下myPromise全家桶,这里终极版本,为了大家看着方便,我就把注释全都取掉了,有不理解的可以返回去对应的标题查看解说,来咯

const RESOLVED = 'RESOLVED'
const REJECTED = 'REJECTED'
const PENDING = 'PENDING'
const resolvePromise = (promise2, x, resolve, reject) => {
  if (promise2 === x) {
    return reject(new TypeError('循环引用报错'))
  }
  if (x !== null && (typeof x === 'object' || typeof x === 'function')) {

    let called;
    try {
      let then = x.then
      if (typeof then === 'function') {
        then.call(x, y => {
          if (called) return
          called = true
          resolvePromise(promise2, y, resolve, reject)
        }, e => {
          if (called) return
          called = true
          reject(e)
        })
      } else {
        resolve(x)
      }
    } catch (e) {
      if (called) return
      called = true
      reject(e)
    }
  } else {
    resolve(x)
  }
}
class Promise {
  constructor(executor) {
    this.status = PENDING
    this.value = undefined
    this.reason = undefined
    this.onfulfilledCallbacks = []
    this.onrejectedCallbacks = []
    let resolve = (value) => {
      if (value instanceof Promise) {
        return value.then(resolve, reject)
      }
      if (this.status === PENDING) {
        this.value = value
        this.status = RESOLVED
        this.onfulfilledCallbacks.forEach(fn => fn())
      }
    }
    let reject = (reason) => {
      if (this.status === PENDING) {
        this.reason = reason
        this.status = REJECTED
        this.onrejectedCallbacks.forEach(fn => fn())
      }
    }

    try {
      executor(resolve, reject)
    } catch (e) {
      reject(e)
    }
  }
  then(onfulfilled, onrejected) {
    onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : v => v;
    onrejected = typeof onrejected === 'function' ? onrejected : err => {
      throw err
    };
    let promise2 = new Promise((resolve, reject) => {
      if (this.status === RESOLVED) {
        setTimeout(() => {
          try {
            let x = onfulfilled(this.value)
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        })
      }
      if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let x = onrejected(this.reason)
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        })
      }
      if (this.status === PENDING) {
        this.onfulfilledCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onfulfilled(this.value)
              resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          })
        })
        this.onrejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onrejected(this.reason)
              resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          })
        })
      }
    })
    return promise2
  }
  catch (onrejected) {
    return this.then(null, onrejected)
  } 
  finally(cb) {
    return this.then(
      (data) => {
        return Promise.resolve(cb()).then(() => data)
      }, (err) => {
        return Promise.resolve(cb()).then(() => { throw err })
      })
  }
  static resolve(value) {
    return new Promise((resolve, reject) => {
      resolve(value)
    })
  }
  static reject(reason) {
    return new Promise((resolve, reject) => {
      reject(reason)
    })
  }
  static all(promises) {
    return new Promise((resolve, reject) => {
      let result = []
      let times = 0
      const processSuccess = (index, val) => {
        result[index] = val
        if (++times === promises.length) {
          resolve(result)
        }
      }
      for (let i = 0; i < promises.length; i++) {
        let p = promises[i];
        if (p && typeof p.then === 'function') {
          p.then(data => {
            processSuccess(i, data)
          }, reject)
        } else {
          processSuccess(i, p)
        }
      }
    })
  }
  static race(promises) {
    return new Promise((resolve, reject) => {
      for (let i = 0; i < promises.length; i++) {
        let p = promises[i];
        if (p && typeof p.then === 'function') {
          p.then(data => {
            resolve(data)
          }, reject)
        } else {
          resolve(p)
        }
      }
    })
  }
}

Promise.defer = Promise.deferred = function () {
  let dfd = {}
  dfd.promise = new Promise((resolve, reject) => {
    dfd.resolve = resolve;
    dfd.reject = reject
  })
  return dfd;
}

module.exports = Promise

🧡🧡🧡Click Me🧡🧡🧡

既然你都看到这了,想必你肯定是感兴趣啦,希望这篇文章对你更加了解Promise有所帮助,后续工作之余还会分享更多实用的技巧和源码分析,点个赞加个关注再走呗

posted @ 2021-04-26 12:58  Echoyya、  阅读(383)  评论(0编辑  收藏  举报