Promise 相关方法模拟实现 (ES6)

464 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第2天,点击查看活动详情

Promise.prototype.finally

finally() 方法返回一个 Promise 。在 promise 结束时,无论结果是 fulfilled 或者是 rejected,都会执行指定的回调函数

那怎么实现呢? 直接往 then 方法塞两个回调不就完事!

/**
 * finally() 方法返回一个Promise。在promise结束时
 * 无论结果是fulfilled或者是rejected,都会执行指定的回调函数
 * @param {function} fn 
 * @returns promise
 */
Promise.prototype.finally = function (fn) {
  return this.then(function (value) {
    return Promise.resolve(fn()).then(function () {
      return value
    })
  }, function (reason){
    return Promise.reject(fn()).then(function () {
      return reason
    })
  }) 
}
// ES6
Promise.prototype.finally = function (fn) {
  return this.then(value => {
    return Promise.resolve(fn()).then(() => value)
  }, reason => {
    return Promise.reject(fn()).then(() => reason)
  })
}

这方法有啥用呢?比如你在处理异步请求的时候,经常会使用 loading,是不是事后无论请求成功或者失败都要把 loading 去掉,一般就是用在无论失败或成功都需要执行的操作

var load = false
new Promise(function (resolve, reject) {
  load = true
  setTimeout(function () {
    console.log('in pending: ' + load) // in pending: true
    if(Math.random() > 0.5){
      resolve()
    }else{
      reject()
    }
  }, 100)
}).then(function(){
  console.log('in fulfilled: ' + load) // in fulfilled: true
}).catch(function(){
  console.log('in rejected: ' + load) // in rejected: true
}).finally(function () {
  load = false
  console.log('in finally: ' + load) // in finally: false
})

Promise.prototype.catch

这个其实没啥好讲的,其实等于 this.then(null, fn)

/**
 * catch() 方法返回一个Promise,并且处理拒绝的情况。
 * @param {function} fn 
 * @returns promise
 */
Promise.prototype.catch = function (fn) {
  return this.then(null, fn)
}

使用方法极其简单

Promise.reject().catch(function(){
  console.log('in catch')
})

Promise.reject & Promise.resolve

/**
 * Promise.reject()方法返回一个带有拒绝原因的Promise对象
 * @param {any} reason 
 */
Promise.reject = function (reason) {
  return new Promise(function(resolve, reject){
    var fn = function () {
      reject(reason)
    }
    if(reason instanceof Promise){
      reason.then(fn, fn)
    }else {
      fn()
    }
  })
}
// ES6
Promise.reject = function (reason) {
  return new Promise((resolve, reject) => {
    const fn = () => reject(reason)
    reason instanceof Promise ? reason.then(fn, fn) : fn()
  })
}
Promise.reject(new Promise(res => {
  console.log('in promise') // in promise
  res(2)
})).then(v => console.log('in then ' + v))
.catch(v => console.log('in catch ', v)) // in catch  Promise { 2 }
/**
 * Promise.resolve(value)方法返回一个以给定值解析后的Promise 对象
 * 如果这个值是一个 promise ,那么将返回这个 promise 
 * @param {any} reason 
 */
Promise.resolve = function (value) {
  if(value instanceof Promise) return value
  return new Promise(function(resolve, reject){
    resolve(value)
  })
}
// ES6
Promise.resolve = function (value) {
  return value instanceof Promise ? value :
    new Promise(resolve => resolve(value))
}
Promise.resolve(new Promise((res, rej) => {
  console.log('in promise') // in promise
  res(2)
})).then(v => console.log('in then ' + v)) // in then 2
.catch(v => console.log('in catch ', v))
// 注意这里会有区别哦!
Promise.resolve(new Promise((res, rej) => {
  console.log('in promise') // in promise
  rej(2)
})).then(v => console.log('in then ' + v))
.catch(v => console.log('in catch ', v)) // in catch 2

接下来就是面试经常问到的四个方法了

Promise.all

Promise.all 是所有 Promise 结果都为 fulfilled 时才会执行 then 方法的回调!

值得注意的是,all 方法接收的是一个迭代器对象,很多实现代码上来就是一个 for 循环的写法是不对的!

/**
 * Promise.all() 方法接收一个 promise 的 iterable 类型的输入
 * 并且只返回一个Promise实例
 * resolve([value,...])
 * reject(firstReject)
 * @param {*} iterable 
 * @returns promise
 */
Promise.all = function (iterable) {
  return new Promise(function(resolve, reject){
    var iteratee = iterable[Symbol.iterator]()
    var len = 0
    var i = 0
    var res = null
    var result = []
    while (true){
      res = iteratee.next()
      if(res.done) break
      !(function(j){
        len++
        var val = !(res.value instanceof Promise) ?  Promise.resolve(res.value) : res.value
        val.then(function(value){
          result[j] = value
          i++
          if(len === i && res.done) resolve(result)
        }, function(reason){
          reject(reason)
        })
      })(len)
    }
  })
};
// ES6
Promise.all = function (iterable) {
  return new Promise((resolve, reject) => {
    const promiseList = [...iterable] // 转换可迭代对象为数组
    const result = []
    let j = 0
    let len = promiseList.length
    promiseList.forEach((val, i) => {
      val = !(val instanceof Promise) ? Promise.resolve(val) : val
      val.then(value => {
        result[i] = value
        j++
        if(len <= j) resolve(result)
      }, reason => reject(reason))
    })
  })
};

测试代码

[
  new Map([[Promise.resolve(1), Promise.resolve(2)]]),
  new Set([Promise.resolve(1), Promise.resolve(2)]), 
  [Promise.resolve(1), Promise.resolve(2)],
  new Set([Promise.reject(1), Promise.resolve(2)]), 
  [Promise.reject(1), Promise.resolve(2)],
  [1, '1', true, false, null, undefined, {}, function(){}, Symbol(), Promise.resolve(2)]
].forEach(item => {
  Promise.all(item).then(v => {
    console.log(v)
  }).catch(r => {
    console.log('in catch ' + r)
  })
})

image.png

Promise.any

Promise.any 是只要有一个 Promise 结果都为 fulfilled 时就会执行 then 方法的回调!

/**
 * Promise.any() 方法接收一个 promise 的 iterable 类型的输入
 * 只要其中的一个 promise 成功,就返回那个已经成功的 promise 
 * resolve([value,...])
 * reject(firstReject)
 * @param {*} iterable 
 * @returns promise
 */
Promise.any = function (iterable) {
  return new Promise(function(resolve, reject){
    var iteratee = iterable[Symbol.iterator]()
    var res = null
    var len = 0
    var i = 0
    while (true){
      res = iteratee.next()
      if(res.done) break
      len++
      var val = !(res.value instanceof Promise) ? Promise.resolve(res.value) : res.value
      val.then(function(value){
        i++
        resolve(value)
      }, function(reason){
        i++ 
        if(i >= len && res.done){
          reject(reason)
        }
      })
    }
  })
};
// ES6
Promise.any = function (iterable) {
  return new Promise((resolve, reject) => {
    const promiseList = [...iterable]
    const len = promiseList.length
    let i = 0
    promiseList.forEach(val => {
      val = !(val instanceof Promise) ? Promise.resolve(val) : val
      val.then(value => {
        i++
        resolve(value)
      }, reason => {
        i++ 
        if(i >= len){
          reject(reason)
        }
      })
    })
  })
};

测试

[
  new Map([[Promise.resolve(1), Promise.resolve(2)]]),
  new Set([Promise.resolve(1), Promise.resolve(2)]), 
  [Promise.resolve(1), Promise.resolve(2)],
  new Set([Promise.reject(1), Promise.resolve(2)]), 
  [Promise.reject(1), Promise.resolve(2)],
  [1, '1', true, false, null, undefined, {}, function(){}, Symbol(), Promise.resolve(2)]
].forEach(item => {
  Promise.any(item).then(v => {
    console.log(v)
  }).catch(r => {
    console.log('in catch ' + r)
  })
})

image.png

Promise.race

Promise.race 是只要有一个 Promise 产生结果时就会执行 then 方法的回调!

/**
 * Promise.race(iterable) 方法返回一个 promise,
 * 一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝
 * resolve([value,...])
 * reject(firstReject)
 * @param {*} iterable 
 * @returns promise
 */
Promise.race = function (iterable) {
  return new Promise(function(resolve, reject){
    var iteratee = iterable[Symbol.iterator]()
    var res = null
    var done = false
    while (true){
      res = iteratee.next()
      if(res.done) break
      var val = !(res.value instanceof Promise) ? Promise.resolve(res.value) : res.value
      val.then(function(value){
        if(done) return
        done = true
        resolve(value)
      }, function(reason){
        if(done) return
        done = true
        reject(reason)
      })
    }
  })
};
Promise.race = function (iterable) {
  return new Promise(function(resolve, reject){
    const promiseList = [...iterable]
    let done = false
    promiseList.forEach(val => {
      val = !(val instanceof Promise) ? Promise.resolve(val) : val
      val.then(value => {
        if(done) return
        done = true
        resolve(value)
      }, reason => {
        if(done) return
        done = true
        reject(reason)
      })
    })
  })
};

image.png

Promise.allSettled

Promise.allSettled 返回一个在所有给定的promise都已经fulfilled或rejected后的promise

/**
 * Promise.allSettled()方法返回一个在所有给定的promise都已经fulfilled或rejected后的promise
 * 并带有一个对象数组,每个对象表示对应的promise结果
 * resolve([value,...])
 * @param {*} iterable 
 * @returns promise
 */
Promise.allSettled = function (iterable) {
  return new Promise(function(resolve){
    var iteratee = iterable[Symbol.iterator]()
    var res = null
    var i = 0
    var len = 0
    var result = []
    var allSettled = function(){
      i++
      if(len <= i && res.done) resolve(result)
    }
    while (true){
      res = iteratee.next()
      if(res.done) break
      !(function(j){
        var val = !(res.value instanceof Promise) ? Promise.resolve(res.value) : res.value
        val.then(function(value){
          result[j] = {
            value: value,
            status: 'fulfilled'
          }
          allSettled()
        }, function(reason){
          result[j] = {
            reason: reason,
            status: 'rejected'
          }
          allSettled()
        })
      }(len))
      len += 1
    }
  })
};
Promise.allSettled = function (iterable) {
  return new Promise(resolve => {
    const promiseList = [...iterable]
    const result = []
    const len = promiseList.length
    let j = 0
    var allSettled = () => len <= ++j && resolve(result)
    promiseList.forEach((val, i) => {
      var val = !(val instanceof Promise) ? Promise.resolve(val) : val
      val.then(value => {
        result[i] = {
          value: value,
          status: 'fulfilled'
        }
        allSettled()
      }, reason => {
        result[i] = {
          reason: reason,
          status: 'rejected'
        }
        allSettled()
      })
    })
  })
};

image.png

搞定收工!

源码地址