promise

392 阅读6分钟

源码

  • Promise的三种状态常量 —— pending:等待态; resolved:成功态; rejected:失败态
  • 链式调用的本质 —— promise的then方法必须返回一个新的promise对象
链式调用的本质:
promise2 = promise1.then(onFulfilled, onRejected)
promise1.then(onFulfilled1, onRejected1).then(onFulfilled2, onRejected2)
源码(promise.js):
function Promise (executor) { // 构造函数Promise必须接受一个函数(executor)作为参数
  this.status = 'pending' // 初始状态为pending
  this.value = undefined // 成功的值
  this.reason = undefined // 失败的原因
  this.onResolvedCallbacks = [] // 保存成功回调函数队列(new Promise()时可能会有异步操作),见[案例4]
  this.onRejectedCallbacks = [] // 保存失败回调函数队列(new Promise()时可能会有异步操作)

  resolve = value => { // resovle时的执行函数(把状态变成resolved)
    if (this.status === 'pending') { // 只有等待态 可以改变状态
      this.value = value
      this.status = 'resolved'
      this.onResolvedCallbacks.forEach(fn => fn()) // 异步执行resolve()时,做【发布】操作,见[案例4]
    }
  }
  reject = reason => { // reject时的执行函数(把状态变成rejected)
    if (this.status === 'pending') { // 只有等待态 可以改变状态
      this.reason = reason
      this.status = 'rejected'
      this.onRejectedCallbacks.forEach(fn => fn()) // 异步执行reject()时,做【发布】操作
    }
  }

  try { // new Promise()时, 直接执行executor函数(同步执行)
    executor(resolve, reject) // executor函数包含resolve和reject两个参数(且都为函数)
  } catch (err) { // 如果执行exectuor时出错,就让当前promise变成失败态
    reject(err)
  }
}

/**
* @param {*} promise2  调用then时返回的【新的】promise对象
* @param {*} x  【当前】then中onFulfilled或onRejected函数的返回值
*        若x不为Promise,则x直接作为Promise2的值(即新的onFulfilled或onRejected函数的参数)
*        若x 为 Promise,则promise2的一个回调函数(resolve或reject),会等待该Promise对象(即x)状态变化后才会被调用,且Promise2的状态和x的状态相同
* @param {*} resolve promise2的resolve
* @param {*} reject  promise2的reject
*/
function resolvePromise (promise2, x, resolve, reject) {  
  if (promise2 === x) { // 不能自己等自己,见[案例1]
    return reject(new TypeError('Chaining cycle detected for promise'))
  }

  let called = false  // 防止多次调用(既调成功也调失败),别人的代码可能没有判断 —— 只有等待态 可以改变状态

  if (x !== null && (typeof x === 'object' || typeof x === 'function')) { //【1+】x是对象或函数才有可能是promise
    try { // 尝试取x的then属性,进一步判别x是否为promise,可能会抛异常(这个promise可能是别人乱写的),见[案例5]
      let t = x.then
   
      if (typeof t === 'function') { //【2+】x.then为函数才可能是promise
        /**
        * x可能是promise,直接让x.then执行
        * 1、promise.then() // this指向promise
        * 2、let t = promise.then
        *    t() // this指向全局,通过t.call(x, ...)将this指向x
        * 
        * 以下相当于:
        *     x.then(y => {}, err => {})
        */
        t.call(x, y => { // promise成功的结果,见[案例7]
          if (called) return // 防止多次调用(既调成功也调失败),别人的代码可能没有判断 —— 只有等待态 可以改变状态
          called = true
          resolvePromise(promise2, y, resolve, reject) // 成功的结果可能是promise,需要【递归】解析,直到解析成普通值为止
        }, err => { // promise失败的结果,见[案例6]
          if (called) return
          called = true
          reject(err)
        })
      } else { //【2-】x不为promise(即为普通值,如:x为{then: {}})
        resolve(x)
      }
    } catch (err) {
      if (called) return
      called = true
      reject(err)
    }
  } else { //【1-】x不为promise(即为普通值)
    resolve(x)
  }
}

Promise.prototype.then = function (onFulfilled, onRejected) { // then是异步调用,此处用setTimeout模仿 (原生的then的成功或失败 是一个微任务)
  /**
  * onFulfilled和onRejected是可选参数,如果onFulfilled或onRejected不是函数,必须被忽略
  * 如果onFulfilled不是函数且promise为成功态,则promise2为成功态并返回promise成功的值,见[案例3]
  * 如果onRejected 不是函数且promise为失败态,则promise2为失败态并返回promise失败的值
  */
  onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
  onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err }

  let promise2 = new Promise((resolve, reject) => { // 每次调用then时必须返回一个新的promise对象
    switch (this.status) { // this指向promise
      case 'resolved':
        setTimeout(() => {
          try { // 如果onFulfilled或onRejected抛出异常,则promise2必须变为失败态,见[案例2]
            let x = onFulfilled(this.value)
            resolvePromise(promise2, x, resolve, reject)
          } catch (err) {
            reject(err)
          }
        }, 0)
        break
      case 'rejected':
        setTimeout(() => {
          try { // 如果onFulfilled或onRejected抛出异常,则promise2必须变为失败态
            let x = onRejected(this.reason)
            resolvePromise(promise2, x, resolve, reject)
          } catch (err) {
            reject(err)
          }
        }, 0)
        break
      case 'pending':
        this.onResolvedCallbacks.push(() => { // 异步执行resolve()时,做【订阅】操作,见[案例4]
          setTimeout(() => {
            try { // 如果onFulfilled或onRejected抛出异常,则promise2必须变为失败态
              let x = onFulfilled(this.value)
              resolvePromise(promise2, x, resolve, reject)
            } catch (err) {
              reject(err)
            }
          }, 0)
        })
        this.onRejectedCallbacks.push(() => { // 异步执行reject()时,做【订阅】操作
          setTimeout(() => {
            try { // 如果onFulfilled或onRejected抛出异常,则promise2必须变为失败态
              let x = onRejected(this.reason)
              resolvePromise(promise2, x, resolve, reject)
            } catch (err) {
              reject(err)
            }
          }, 0)
        })
        break
    }
  })
  return promise2
}
测试代码:
Promise.defer = Promise.deferred = function () {
  let dfd = {}
  dfd.promise = new Promise((resolve, reject) => {
    dfd.resolve = resolve
    dfd.reject = reject
  })
  return dfd
}
module.exports = Promise

// npm install promises-aplus-tests -g
// promises-aplus-tests promise.js
案例1:promise2和onFulfilled或onRejected函数返回的结果是同一个对象
let promise = new Promise((resolve, reject) => {
  resolve()
})

let promise2 = promise.then(res => { // 不能自己等自己
  return promise2
})

// TypeError: Chaining cycle detected for promise
案例2:如果onFulfilled或onRejected抛出异常,则promise2必须变为失败态
let promise = new Promise((resolve, reject) => {
  resolve()
})
promise2 = promise.then(res => {
  throw new Error('这里抛出一个异常')
})
promise2.then(res => {
  console.log(res)
}, err => {
  console.log(err) // 这里抛出一个异常
})
案例3:如果onFulfilled不是函数且promise为成功态,则promise2为成功态并返回promise成功的值
let promise = new Promise((resolve, reject) => {
  resolve('success')
})
promise2 = promise.then('这里的onFulfilled本来是一个函数,但现在不是')
promise2.then(res => {
  console.log(res) // success
}, err => {
  console.log(err)
})
案例4:异步执行resolve()时,做【发布、订阅】操作
非异步:
let promise = new Promise((resolve, reject) => {
  resolve('成功') // 非异步执行
})
promise.then(res => { // 状态为resolved,值为成功,直接执行
  console.log(res) // 成功
})

异步:
let promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('成功') // 异步执行
  }, 1000)
})
promise.then(res => { // 状态为pending,值为undefined,执行【订阅】操作;1s后状态为resolved,值为成功,执行【发布】操作
  console.log(res) // 1s后,成功
})
案例5:尝试取x的then属性,进一步判别x是否为promise时,可能会抛异常
let obj = {}
Object.defineProperty(obj, 'then', {
  get () {
    throw new Error('出错')
  }
})
console.log(obj.then) // 出错
案例6:x.then失败
let promise = new Promise((resolve, reject) => {
  resolve()
})
let promise2 = promise.then(res => {
  return new Promise((resolve, reject) => {
    reject('失败')
  })
})
promise2.then(res => {
  console.log(res)
}, err => {
  console.log(err) // 失败
})
案例7:x.then成功
let promise = new Promise((resolve, reject) => {
  resolve()
})
let promise2 = promise.then(res => {
  return new Promise((resolve, reject) => {
    resolve(new Promise((resolve, reject) => {
      resolve('成功')
    }))
  })
})
promise2.then(res => {
  console.log(res) // 成功
}, err => {
  console.log(err)
})

应用

let fs = require('fs')

function read (filePath, encoding) {
  return new Promise((resolve,reject) => {
    fs.readFile(filePath, encoding, (err, data) => {
      if(err) reject(err)
      resolve(data)
    })
  })
}

read('1.txt', 'utf8').then(res => {
  return read(res, 'utf8')
}).then(res => {
  return read(res, 'utf8')
}).then(res => {
  console.log(res) // hello
})

// 1.txt内容 2.txt
// 2.txt内容 3.txt
// 3.txt内容 hello

catch(catch的实现基于then,可理解为不传成功的then)

Promise.prototype.catch = function (onRejected) {
  return this.then(null, onRejected)
}

resolve

Promise.resolve = function (value) {
  return new Promise((resolve, reject) => {
    resolve(value)
  })
}

reject

Promise.reject = function (reason) {
  return new Promise((resolve, reject) => {
    reject(reason)
  })
}

all

Promise.all = function (promises) {
  return new Promise((resolve, reject) => {
    let arr = []
    let i = 0
    function processData (index, data) {
      arr[index] = data
      if (++i == promises.length) {
        resolve(arr)
      }
    }
    for (let i = 0; i < promises.length; i++) {
      promises[i].then(data => { // data是成功的结果
        processData(i, data)
      }, reject)
    }
  })
}

race

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