Promise

Promise A+ 规范 #

  • 官方英文地址:https://promisesaplus.com/
  • 中文翻译可参考 http://malcolmyu.github.io/malnote/2015/06/12/Promises-A-Plus/

代码实现 #

  • github地址:https://github.com/whu-luojian/interview-questions/blob/master/%E6%89%8B%E5%86%99%E4%BB%A3%E7%A0%81/promise.js
// Promise/A+ 规范规定的三种状态
const STATUS = {
  PENDING: 'pending',
  FULFILLED: 'fulfilled',
  REJECTED: 'rejected'
}

class MyPromise {
  // 构造函数接收一个执行回调
  constructor(executor) {
    this._status = STATUS.PENDING // Promise初始状态
    this._value = undefined // then回调的值
    this._resolveQueue = [] // resolve时触发的成功队列
    this._rejectQueue = [] // reject时触发的失败队列

    // 使用箭头函数固定this(resolve函数在executor中触发,不然找不到this)
    const resolve = value => {
      const run = () => {
        // Promise/A+ 规范规定的Promise状态只能从pending触发,变成fulfilled
        if (this._status === STATUS.PENDING) {
          this._status = STATUS.FULFILLED // 更改状态
          this._value = value // 储存当前值,用于then回调

          // 执行resolve回调
          while (this._resolveQueue.length) {
            const callback = this._resolveQueue.shift()
            callback(value)
          }
        }
      }
      //把resolve执行回调的操作封装成一个函数,放进setTimeout里,以实现promise异步调用的特性(规范上是微任务,这里是宏任务)
      setTimeout(run)
    }

    // 同 resolve
    const reject = value => {
      const run = () => {
        if (this._status === STATUS.PENDING) {
          this._status = STATUS.REJECTED
          this._value = value

          while (this._rejectQueue.length) {
            const callback = this._rejectQueue.shift()
            callback(value)
          }
        }
      }
      setTimeout(run)
    }

    // new Promise()时立即执行executor,并传入resolve和reject
    executor(resolve, reject)
  }

  // then方法,接收一个成功的回调和一个失败的回调
  then(onFulfilled, onRejected) {
    // 根据规范,如果then的参数不是function,则忽略它, 让值继续往下传递,链式调用继续往下执行
    typeof onFulfilled !== 'function' ? onFulfilled = value => value : null
    typeof onRejected !== 'function' ? onRejected = error => error : null

    // then 返回一个新的promise
    return new MyPromise((resolve, reject) => {
      const resolveFn = value => {
        try {
          const x = onFulfilled(value)
          // 分类讨论返回值,如果是Promise,那么等待Promise状态变更,否则直接resolve
          x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
        } catch (error) {
          reject(error)
        }
      }

      const rejectFn = error => {
        try {
          const x = onRejected(error)
          x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
        } catch (error) {
          reject(error)
        }
      }

      switch (this._status) {
        case STATUS.PENDING:
          this._resolveQueue.push(resolveFn)
          this._rejectQueue.push(rejectFn)
          break;
        case STATUS.FULFILLED:
          resolveFn(this._value)
          break;
        case STATUS.REJECTED:
          rejectFn(this._value)
          break;
      }
    })
  }

  catch(rejectFn) {
    return this.then(undefined, rejectFn)
  }

  finally(callback) {
    return this.then(value => MyPromise.resolve(callback()).then(() => value), error => {
      MyPromise.resolve(callback()).then(() => error)
    })
  }

  // 静态resolve方法
  static resolve(value) {
    return value instanceof MyPromise ? value : new MyPromise(resolve => resolve(value))
  }

  // 静态reject方法
  static reject(error) {
    return new MyPromise((resolve, reject) => reject(error))
  }

  // 静态all方法
  static all(promiseArr) {
    let count = 0
    let result = []
    return new MyPromise((resolve, reject) => {
      if (!promiseArr.length) {
        return resolve(result)
      }
      promiseArr.forEach((p, i) => {
        MyPromise.resolve(p).then(value => {
          count++
          result[i] = value
          if (count === promiseArr.length) {
            resolve(result)
          }
        }, error => {
          reject(error)
        })
      })
    })
  }

  // 静态race方法
  static race(promiseArr) {
    return new MyPromise((resolve, reject) => {
      promiseArr.forEach(p => {
        MyPromise.resolve(p).then(value => {
          resolve(value)
        }, error => {
          reject(error)
        })
      })
    })
  }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152