一、Promise的使用

创建一个promise实例,方便处理异步操作

var p = new Promise(function(resolve,reject){
    setTimeout(function(){
       resolve("success")
    },1000);
    console.log("创建一个新的promise");
})
p.then(function(x){
  console.log(x)
})

// 输出:创建一个新的promise
// 输出:success

promise.then()链式调用

var p = new Promise(function(resolve, reject) {
  resolve()
});
p.then(...).then(...).then(...)

其他方法:

  • Promise.resolve()
  • Promise.all()
  • Promise.race()

二、Promise原理剖析

1.Promise 凭借什么消灭了回调地狱

2.为什么Promise要引入微任务

3.Promise 如何实现链式调用

正文。。。


三、手写Promise/A+规范的Promise

Promise/A+规范:
『术语』:

  • promise是一个对象或者函数,该对象或者函数有一个then方法
  • thenable是一个对象或者函数,用来定义then方法
  • valuepromise状态成功时的值
  • reasonpromise状态失败时的值

要求:

  • promise必须有3个状态,pendingfulfilled(resolved)rejected
    • 当处于pending状态的时候,可以转移到fulfilled(resolved)rejected状态
    • 当处于fulfilled(resolved)状态或rejected状态的时候,则不可变
  • 一个promise必须有一个then方法
    • then方法接受两个参数:promise.then(onFulfilled, onRejected)
      • onFulfilled方法表示状态从pending——>fulfilled(resolved)时所执行的方法
      • onRejected表示状态从pending——>rejected所执行的方法。
  • 为了实现链式调用,then方法必须返回一个promise。promise2=promise1.then(onFulfilled,onRejected)

1.Promise基础结构

  • 执行回调数组:当resolve()延迟执行时,Promise的状态为pending,不应立即执行成功调用的函数,需要把它存起来,直到执行resolve再执行成功调用的函数
  • 链式调用:Promise里的链接调用是返回一个新的Promise对象
  • 异步执行:用setTimeout模拟微任务(JS事件循环执行顺序上和原生Promise有区别),把回调放入,等待确保异步执行

基础Promise

const isFunction = variable => typeof variable === 'function'

// 定义三个常量表示状态
const PENDING = 'pending'
const FULFILLED = 'fulfilled',
const REJECTED = 'rejected'

class MyPromise {
  constructor(func) {
    this.onFulfilledArray = [] // 成功回调数组
    this.onRejectedArray = [] // 失败回调数组
    this.value = void 0
    this.reason = void 0
    this.status = PENDING
    func(this.resolve.bind(this), this.reject.bind(this))
  }
  resolve(val) {
    if (this.status !== PENDING) return
    const self = this
    const doResolve = () => {
      if (self.status === PENDING) {
        self.status = FULFILLED
        self.value = val
        // resolve里面将所有成功的回调拿出来执行
        while (self.onFulfilledArray.length) {
          const fn = self.onFulfilledArray.shift()
          fn()
        }
      }
    }
    setTimeout(doResolve(), 0)
  }
  reject(err) {
    if (this.status !== PENDING) return
    const self = this
    const doReject = () => {
      if (self.status === PENDING) {
        self.status = REJECTED
        self.reason = err
        // resolve里面将所有失败的回调拿出来执行
        self.onRejectedArray.forEach(fn => {
          fn()
        })
      }
    }
    setTimeout(doReject, 0)
  }
  then(onFulfilled, onRejected) {
    let nextPromise = void 0
    switch (this.status) {
      case PENDING:
        nextPromise = new MyPromise((resolve, reject) => {
          this.onFulfilledArray.push(() => {
            try {
              let res = onFulfilled(self.value)
              resolve(res)
            } catch (error) {
              reject(error)
            }
          })
          this.onRejectedArray.push(() => {
            try {
              let res = onRejected(self.reason)
              resolve(res)
            } catch (e) {
              reject(e)
            }
          })
        })
        break
      case FULFILLED:
        nextPromise = new MyPromise((resolve, reject) => {
          try {
            let res = onFulfilled(self.value)
            resolve(res)
          } catch (e) {
            reject(e)
          }
        })
        break
      case REJECTED:
        nextPromise = new MyPromise((resolve, reject) => {
          try {
            let res = onRejected(self.reason)
            resolve(res)
          } catch (e) {
            reject(e)
          }
        })
        onRejected(self.reason)
        break
    }
    return nextPromise
  }
  catch(err) {
    // 默认没有成功,只有失败
    return this.then(void 0, err)
  }
}

2.链式调用进阶版

以上的Promise存在一个问题,如果链式调用中Promise返回的是普通值,应该把值包装成新的Promise对象

要求:

  • 每个then方法都返回一个新的Promise对象(重点)
  • 如果then方法返回了一个Promise对象,则需要查看它的状态
    • 如果状态是成功,则调用resolve方法,把成功的状态传递给它;
    • 如果是失败的,则把失败的状态传递给下一个Promise对象
  • 如果then方法中返回的是一个原始数据类型值(如 Number、String 等)就使用此值包装成一个新的Promise对象返回
  • 如果then方法中没有return语句,则返回一个用undefined包装的Promise对象
  • 如果 then 方法没有传入任何回调,则继续向下传递(值的传递特性)
  • 如果是循环引用则需要抛出错误

修改then方法,增加resolvePromise函数

...
function resolvePromise(x, nextPromise, resolve, reject) {
  // 处理三种情况:
  // 1.循环引用
  // 2.x 为MyPromise
  // 3.x为基础类型
  if (x === nextPromise) {
    // x 和 nextPromise 指向同一对象,循环引用抛出错误。防止死循环
    return reject(new TypeError('循环引用'))
  } else if (x && (typeof x === 'object' || isFunction(x))) {
    // x 是对象或者函数
    let called = false // 避免多次调用
    try {
      let then = x.then // 判断对象是否有 then 方法
      if (isFunction(then)) {
        // then 是函数,就断定 x 是一个 MyPromise(根据Promise A+规范)
        then.call(
          x,
          function(y) {
            // 调用返回的MyPromise,用它的结果作为下一次then的结果
            if (called) return
            called = true
            resolvePromise(y, nextPromise, resolve, reject) // 递归解析成功后的值,直到它是一个普通值为止
          },
          function(r) {
            if (called) return
            called = true
            reject(r) // 取then时发生错误了
          }
        )
      } else {
        resolve(x) // 此时,x是一个普通对象
      }
    } catch (e) {
      reject(e)
    }
  } else {
    // x 是原始数据类型 / 没有返回值,这里即是undefined
    resolve(x)
  }
}
class MyPromise {
  constructor(func) {
    ...
  }
  ...
  then(onFulfilled, onRejected) {
    const self = this
    // 如果onFulfilled不是函数,给一个默认函数,返回value
    let realOnFulfilled = onFulfilled
    if (!isFunction(realOnFulfilled)) realOnFulfilled = value => value
    let realOnRejected = onRejected
    if (!isFunction(onRejected)) {
      realOnRejected = reason => {
        if (reason instanceof Error) {
          throw reason
        } else {
          throw new Error(reason)
        }
      }
    }
    let nextPromise = void 0
    switch (this.status) {
      case PENDING:
        nextPromise = new MyPromise((resolve, reject) => {
          this.onFulfilledArray.push(() => {
            try {
              let res = realOnFulfilled(self.value)
              resolvePromise(res, nextPromise, resolve, reject)
              // resolve(res)
            } catch (error) {
              reject(error)
            }
            // onFulfilled(self.value)
          })
          this.onRejectedArray.push(() => {
            try {
              let res = realOnRejected(self.reason)
              resolve(res)
            } catch (e) {
              reject(e)
            }
            // onRejected(self.reason)
          })
        })
        break
      case FULFILLED:
        nextPromise = new MyPromise((resolve, reject) => {
          try {
            let res = realOnFulfilled(self.value)
            resolvePromise(res, nextPromise, resolve, reject)
          } catch (e) {
            reject(e)
          }
        })
        // onFulfilled(self.value)
        break
      case REJECTED:
        nextPromise = new MyPromise((resolve, reject) => {
          try {
            let res = realOnRejected(self.reason)
            resolvePromise(res, nextPromise, resolve, reject)
          } catch (e) {
            reject(e)
          }
        })
        onRejected(self.reason)
        break
    }
    return nextPromise
  }
  ...
}

3.Promise.prototype.finally实现

有两大重点:

  • 无论当前这个 Promise 对象最终的状态是成功还是失败 ,finally 方法里的回调函数都会执行一次
  • 在 finally 方法后面可以继续链式调用 then 方法,拿到当前这个 Promise 对象最终返回的结果

挂载在MyPromise.property原型上,或者类似catch实现:

MyPromise.prototype.finally = function(callback) {
  return this.then(
    data => {
      return MyPromise.resolve(callback()).then(() => data)
    },
    err => {
      return MyPromise.resolve(
        callback().then(() => {
          throw err
        })
      )
    }
  )
}

4.Promise.all()Promise.race()实现

Promise.all():可将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值

...
function isPromise(val) {
  return val && typeof val.then === 'function'
}
class MyPromise() {
  ...
}
...
MyPromise.all = function(promises) {
  if (!Array.isArray(promises)) {
    throw new Error('Not a array')
  }
  return new MyPromise((resolve, reject) => {
    let result = []
    let times = 0 // 计数器
    function processData(index, val) {
      result[index] = val
      if (++times === promises.length) {
        resolve(result)
      }
    }
    for (let i = 0; i < promises.length; i++) {
      let p = promises[i]
      if (isPromise(p)) {
        // MyPromise对象
        p.then(data => {
          processData(i, data)
        }, reject)
      } else {
        processData(i, p) // 普通值
      }
    }
  })
}

Promise.race()在执行多个异步操作中,不管结果是成功状态还是失败状态,只保留取第一个执行完成的异步操作的结果。其他的方法仍会执行,但结果会被抛弃

...
class MyPromise() {
  ...
}
...
MyPromise.race = function(promises) {
  if (!Array.isArray(promises)) {
    throw new Error('Not a array')
  }
  return new MyPromise((resolve, reject) => {
    if (promises.length === 0) {
      return
    } else {
      for (let p of promises) {
        p.then(
          value => {
            resolve(value)
          },
          reason => {
            reject(reason)
          }
        )
      }
    }
  })
}

5.最终手写版源码

代码太长,直接参考源码仓库 examples | lianpf.github。欢迎star


参考

思路很清晰的两篇文章:


最后, 希望大家早日实现:成为编程高手的伟大梦想!
欢迎交流~

微信公众号

本文版权归原作者曜灵所有!未经允许,严禁转载!对非法转载者, 原作者保留采用法律手段追究的权利!
若需转载,请联系微信公众号:连先生有猫病,可获取作者联系方式!