实现的Promise,符合Promise/A+规范。

const STATUS_PENDING = 'pending'
const STATUS_FULFILLED = 'fulfilled'
const STATUS_REJECTED = 'rejected'

class Promise {
  constructor (executer) {
    // const that = this
    const reject = (reason) => {
      if (this.status !== STATUS_PENDING) return

      this.reason = reason
      this.status = STATUS_REJECTED
      this.rejectedCallbacks.forEach((rejectedCallback) => rejectedCallback())
    }
    const resolve = (value) => {
      if (this.status !== STATUS_PENDING) return

      // if ((typeof value === 'object' || typeof value === 'function') && typeof value.then === 'function') {
      //   return resolve(value)
      // }
      // 这里是用于在new Promise中resolve一个promise实例,进行递归等待内部所有promise实例状态转为fulfilled或者rejected
      if (value instanceof Promise) {
        return value.then(resolve, reject)
      }
      this.value = value
      this.status = STATUS_FULFILLED
      this.resolvedCallbacks.forEach((resolvedCallback) => resolvedCallback())
    }

    this.status = STATUS_PENDING
    this.resolvedCallbacks = []
    this.rejectedCallbacks = []

    try {
      executer(resolve, reject)
    } catch (e) {
      reject(e)
    }
  }

  then (onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value // p.then().then().then()
    onRejected = typeof onRejected === 'function' ? onRejected : (err) => { throw err }

    // const that = this // 因为下面用了箭头函数,自动找作用域链上一层,所以不用这个that。
    // 将所有then中的方法放到setTimeout的原因是:这些方法默认要异步执行。例子见./cases/async-then-fn.html

    const promise2 = new Promise((resolve, reject) => {
      if (this.status === STATUS_PENDING) {
        this.resolvedCallbacks.push(() => {
          setTimeout(() => {
            try {
              // 这里拿到返回值,是为了then的链式调用。之前then中的return值作为下一个then的入参
              const x = onFulfilled(this.value)
              // resolve(x) 不能简单用resolve,是因为有可能返回的是一个promise实例,所以我们需要判定。
              // 按规范需要等该promise实例的状态转为fulfilled或者rejected,才能进入下一个then
              // 有人会问,我不能直接返回一个promise值吗?
              // 可以呀,没人拦着你,但是规范中要求的是刚刚描述的,在实际生产中确实也好用。
              this.resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          })
        })
        this.rejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              const x = onRejected(this.reason)
              this.resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          })
        })
      }
      if (this.status === STATUS_FULFILLED) {
        setTimeout(() => {
          try {
            const x = onFulfilled(this.value)
            this.resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        })
      }
      if (this.status === STATUS_REJECTED) {
        setTimeout(() => {
          try {
            const x = onRejected(this.reason)
            // 这里的resolv统一翻译为解析
            this.resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        })
      }
    })
    return promise2
  }

  // x是调用方resolve传进来的,需要考虑到别人的promise可能即可以调用成功也可以调用失败,
  // 也就是在构造函数那里不判断状态是否为pending.
  // 所以,我们需要在这里面加一个判断条件,来判断是否已经执行过resolve(也就是调用过resolvedCallbacks)或者resolve(也就是rejectedCallbacks)
  // 如果执行过,我这边就不再执行订阅(也就是存在callBacks中的那些函数)
  resolvePromise (promise2, x, resolve, reject) {
    // 不论是onFullilled或者onRejected返回的,都是x。
    if (x === promise2) {
      reject(new TypeError('返回的promise2在等待自己的状态从pending转为其他,做不到呀~')) // 规定这里必须是TypeError,typeerror表示非预期类型错误
    }
    // 是一个promise实例
    if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
      // 这个y也就是then方法中创造的promise实例resolve的值。
      // 在这里可以递归调用,因为如果每次resolve的值都是一个promise对象,每次调用then都会创造一个promise2,来等待resolve的值从pending转到其他状态。
      // 将最外层的promise2及他的resolve和reject传进去,造成的结果就是一层等待完了,继续等待下一层。我在等待的时候并不知道下一层resolve的值会是什么。

      // 举个例子:当第一个promise实例的状态从pending转为其他状态,那就会执行下面这个resolvePromise。
      // 现在我又拿到了y,还是一个promise实例。那最外层的promise2就会转为等待这个promise实例的状态从pending转为其他。
      let isCalled = false
      try {
        // x是调用方resolve传进来的,所以需要考虑到调用报错的情况。比如
        /* Object.defineProperty(x, 'then', {
          get : function(){
            throw new Error('错误')
          },
          set : function(newValue){
            bValue = newValue;
          },
        }) */
        let then = x.then // 为什么必须把x.then的引用存储到变量上?
        // 按文档中说的,如果下面也是用x.then的方式引用的话,其值容易改变。但是这代码是同步的,怎么会被改变呢?
        if (typeof then === 'function') {
          then.call(x,
            (y) => {
              if (isCalled) return
              isCalled = true
              this.resolvePromise(promise2, y, resolve, reject)
            },
            (reason) => {
              if (isCalled) return
              isCalled = true
              reject(reason)
            }
          )
        } else {
          resolve(x)
        }
      } catch (err) {
        if (isCalled) return
        isCalled = true
        reject(err)
      }
    } else {
      resolve(x)
    }
  }

  catch (onRejected) {
    return this.then(null, onRejected)
  }

  finally (callback) {
    // 将
    let P = this.constructor
    return this.then(
      value => P.resolve(callback()).then(() => value),
      reason => P.resolve(callback()).then(() => { throw reason })
    )
  }

  static deferred () {
    const deferred = {}
    deferred.promise = new Promise((resolve, reject) => {
      deferred.resolve = resolve
      deferred.reject = reject
    })
    return deferred
  }

  static resolve (value) {
    return new Promise((resolve) => resolve(value))
  }

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

  static all (promises) {
    return new Promise((resolve, reject) => {
      // 注意这里用计数器,而不是在下面判断result数组的长度是否和promises相等
      // 是因为var a = []; a[5] = 1 // a.length === 6
      let counter = 0
      let result = []
      let len = promises.length

      function processData (data, i) {
        result[i] = data
        if (++counter === len) {
          resolve(result)
        }
      }

      for (let i = 0; i < len; ++i) {
        const currentP = promises[i]
        if (currentP !== null && (typeof currentP === 'object' || typeof currentP === 'function') && typeof currentP.then === 'function') {
          promises[i].then((data) => {
            processData(data, i)
          }, reject) // 这里简写为reject,不用一个函数内部调用reject了。
        } else {
          processData(currentP, i)
        }
      }
    })
  }

  static race (promises) {
    return new Promise((resolve, reject) => {
      for (let i = 0, len = promises.length; i < len; ++i) {
        const p = promises[i]
        if (p !== null && (typeof p === 'object' || typeof p === 'function') && typeof p.then === 'function') {
          promises[i].then((data) => {
            resolve(data)
          }, reject)
        } else {
          resolve(p)
        }
      }
    })
  }
}

module.exports = Promise

promise题目

Promise.resolve(1)
.then(() => 2)
.catch(() => 3)
.then((d) => {console.log(d)})

Promise.resolve(1)
  .then(x => x + 1)
  .then(() => { throw new Error('asda') })
  .catch(() => 1)
  .then((x) => x + 1)
  .then((x) => console.log(x))

async题目

// 调整后的顺序 script start async1 start async2 promise1 script end async1 end promise2 promise3 promise4 promise5 settimeout

// 调整前的顺序 script start async1 start async2 promise1 script end promise2  promise3 async1 end promise4 promise5 settimeout
async function async1(){
  console.log('async1 start')
  await async2()
  console.log('async1 end')
}
async function async2(){
  console.log('async2')
}
console.log('script start')
setTimeout(function(){
  console.log('setTimeout') 
},0) 
async1();
new Promise(function(resolve){
  console.log('promise1')
  resolve();
}).then(function(){
  console.log('promise2')
}).then(function() {
  console.log('promise3')
}).then(function() {
  console.log('promise4')
}).then(function() {
  console.log('promise5')
})
console.log('script end')

// 老版本不是转成这样
new Promise((resolve) => {
  async2.then(() => resolve())
}).then(() => {
  console.log('async1 end')
})
// 老版本转成这样
new Promise((resolve) => {
  Promise.resolve().then(() => {
    async2.then(() => resolve())
  })
}).then(() => {
  console.log('async1 end')
})
// 新版本转成这样
Promise.resolve(async2()).then(() => {
  console.log('async1 end')
})
// 根据规范,相当于
async2().then(() => {
  console.log('async1 end')
})

再来一个

setImmediate(function () {
      console.log(6)
    })

    new Promise(function (resolve) {
      console.log(1)
      for (let i = 0; i < 9999; ++i) {
        i === 9998 && resolve()
      }
      console.log(2)
    }).then( function () {
      console.log(5)
    })

    var a = +new Date();
    setTimeout(function () {
      console.log('时间:', +new Date() - a)
      console.log(4)
    }, 0)

    console.log(3)

// 如果是node环境下,有两种可能

// 1, 2, 3, 5, 6, 4

// 1, 2, 3, 5, 4, 6

// 如果是浏览器IE

// 1, 2, 3, 5, 4, 6

// 1. new Promise的时候,传进去的方法会同步执行,所以先输出1
// 2. resolve()执行时,将then中的回调函数加入microtask队列,这时候继续执行输出2
// 3. setTimeout中的回调函数到给定时间了才会被放入Event Loop中的timer quewe中,所以先继续往下走输出3
// 4. 这时候,栈中的代码执行完毕。
// 5. 接下来先会清空一遍microtask队列。所以输出5
// 6. 在node中,setTimeout(() => ,0),会被转化为setTimeout(() => {},1)。这个时候要看机器性能优劣了。如果机器性能特别好,比如花了0.00001毫秒就进入了Event loop,那么进入Event Loop的timer quewe阶段的时候,发现还没到时间(1ms),那么Event Loop就会往下走,到check阶段,这时候setImmediate的回调就会先执行。如果机器性能比较差或者环境不稳定,过了1秒钟才进入Event loop,那么进入Event loop的timer quewe阶段,发现settimeout的回调函数已经被放进来了。就会先执行,之后再在check阶段执行setImmediate的回调。

// 如果在浏览器端,基本思路还是Event Loop那一套(不过和node有差异),那么我默认为是IE10以上,因为setImmediate在MDN中说只有ie10实现了。

// 1. 按照大家一贯的说法就是setImmediate比setTimeout快(他俩都是macro task),包括vue源码中也是这么写的。
// 2. 但是我的虚拟机测试得到的结果是setImmediate在ie11和edge都有实现,并且setImmediate比setTimeout要慢。所以我的浏览器执行顺序是1、2、3、5、4、6

参考

segmentfault,写得很细致,墙裂推荐