promise扩展方法

catch

catch其实就是then的第二个参数

1
2
3
MyPromise.prototype.catch = function (onRejected) {
  return this.then(null, onRejected)
}

resolve

  1. 返回一个以给定值解析后的Promise对象
  2. 如果这个值是一个 promise ,那么将返回这个 promise
  3. 如果这个值是thenable(即带有then 方法),返回的promise会“跟随”这个thenable的对象,采用它的最终状态
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
MyPromise.resolve = function (value) {
  if (value instanceof MyPromise) return value
  if (typeof value === 'object' || typeof value === 'function') {
    try {
      let then = value.then
      if (typeof then === 'function') {
        return new MyPromise(then.call(value))
      }
    } catch (e) {
      return new MyPromise((resolve, reject) => {
        reject(e)
      })
    }
  }
  return value
}

reject

1
2
3
4
5
MyPromise.reject = function (reason) {
  return new MyPromise((resolve, reject) => {
    reject(reason)
  })
}

finally

  1. 如果返回一个 promise 会等待这个 promise 也执行完毕。
  2. 如果返回的是成功的 promise,会采用上一次的结果;
  3. 如果返回的是失败的 promise,会用这个失败的结果,传到 catch 中。
1
2
3
4
5
6
7
8
9
MyPromise.prototype.finally = function (callback) {
  return this.then(
    (value) => MyPromise.resolve(callback()).then(() => value),
    (reason) =>
      MyPromise.resolve(callback()).then(() => {
        throw reason
      })
  )
}

race

race接受一个数组,只要有一个请求完成,就返回。将数组的每一项用MyPromise.resolve包裹下,处理thenable

1
2
3
4
5
6
7
8
9
10
11
MyPromise.race = function (promiseList) {
  if (!Array.isArray(promiseList)) {
    return new TypeError(`${promiseList}需要是数组`)
  }
  return new MyPromise((resolve, reject) => {
    if (!promiseList.length) return
    for (let i = 0; i < promiseList.length; i++) {
      MyPromise.resolve(promiseList[i]).then(resolve, reject)
    }
  })
}

all

allrace差不多,接受一个数组,但是要等每一项都返回,并且要保证返回结果和入参数组下标一一对应。如果有一项失败就直接reject

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
MyPromise.all = function (promiseList) {
  if (!Array.isArray(promiseList)) {
    return new TypeError(`${promiseList}需要是数组`)
  }
  return new MyPromise((resolve, reject) => {
    if (!promiseList.length) return
    let resultArr = []
    let count = 0
    for (let i = 0; i < promiseList.length; i++) {
      MyPromise.resolve(promiseList[i]).then((res) => {
        resultArr[i] = res
        if (++count === promiseList.length) {
          resolve(resultArr)
        }
      }, reject)
    }
  })
}

完整代码

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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
// Promise 的状态
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
  constructor(fn) {
    // 状态
    this._status = PENDING
    // 据因
    this.reason = null
    // 终值
    this.value = null
    // 成功回调数组
    this.onFilFulledCallbacks = []
    // 失败回调数组
    this.onRejectedCallbacks = []

    const resolve = (value) => {
      if (this.status === PENDING) {
        this.value = value
        this.status = FULFILLED
      }
    }
    const reject = (reason) => {
      if (this.status === PENDING) {
        this.reason = reason
        this.status = REJECTED
      }
    }
    try {
      fn(resolve, reject)
    } catch (error) {
      reject(error)
    }
  }

  get status() {
    return this._status
  }

  set status(newStatus) {
    this._status = newStatus
    switch (newStatus) {
      case FULFILLED:
        // 执行resolve的时候,如果有缓存的成功回调,全部按顺序执行
        this.onFilFulledCallbacks.forEach((fn) => {
          fn(this.value)
        })
        break
      case REJECTED:
        // 执行reject的时候,如果有换成的失败回调,全部按顺序执行
        this.onRejectedCallbacks.forEach((fn) => {
          fn(this.reason)
        })
        break
    }
  }

  //   一个 promise 必须提供一个 then 方法以访问其当前值、终值和据因。
  then(onFulfilled, onRejected) {
    // 参数可选 并且不是函数将被忽略, 我们重写下两个方法,如果不是function就给个默认的function
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (value) => value
    onRejected =
      typeof onRejected === 'function'
        ? onRejected
        : (error) => {
            throw error
          }

    const promise2 = new MyPromise((resolve, reject) => {
      const fulfilledMicrotask = () => {
        // 我们使用queueMicrotask,使其异步,并进入微任务队列
        queueMicrotask(() => {
          try {
            const x = onFulfilled(this.value)
            this.resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        })
      }
      const rejectedMicrotask = () => {
        queueMicrotask(() => {
          try {
            const x = onRejected(this.reason)
            this.resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        })
      }
      // 如果状态已经被改变,则直接调用
      if (this.status === FULFILLED) {
        fulfilledMicrotask()
      }
      if (this.status === REJECTED) {
        rejectedMicrotask()
      }
      // 如果状态还未改变,缓存到数组中
      if (this.status === PENDING) {
        this.onFilFulledCallbacks.push(fulfilledMicrotask)
        this.onRejectedCallbacks.push(rejectedMicrotask)
      }
    })
    //then 方法可以被同一个 promise 调用多次
    return promise2
  }

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

  static resolve(value) {
    if (value instanceof MyPromise) return value
    if (typeof value === 'object' || typeof value === 'function') {
      try {
        let then = value.then
        if (typeof then === 'function') {
          return new MyPromise(then.call(value))
        }
      } catch (e) {
        return new MyPromise((resolve, reject) => {
          reject(e)
        })
      }
    }
    return new MyPromise((resolve) => {
      resolve(value)
    })
  }

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

  static race(promiseList) {
    if (!Array.isArray(promiseList)) {
      return new TypeError(`${promiseList}需要是数组`)
    }
    return new MyPromise((resolve, reject) => {
      if (!promiseList.length) return
      for (let i = 0; i < promiseList.length; i++) {
        MyPromise.resolve(promiseList[i]).then(resolve, reject)
      }
    })
  }

  static all(promiseList) {
    if (!Array.isArray(promiseList)) {
      return new TypeError(`${promiseList}需要是数组`)
    }
    return new MyPromise((resolve, reject) => {
      if (!promiseList.length) return
      let resultArr = []
      let count = 0
      for (let i = 0; i < promiseList.length; i++) {
        MyPromise.resolve(promiseList[i]).then((res) => {
          resultArr[i] = res
          if (++count === promiseList.length) {
            resolve(resultArr)
          }
        }, reject)
      }
    })
  }

  // 如果返回一个 promise 会等待这个 promise 也执行完毕。
  // 如果返回的是成功的 promise,会采用上一次的结果;
  // 如果返回的是失败的 promise,会用这个失败的结果,传到 catch 中。
  finally(callback) {
    return this.then(
      (value) => Promise.resolve(callback()).then(() => value),
      (reason) =>
        Promise.resolve(callback()).then(() => {
          throw reason
        })
    )
  }

  resolvePromise(promise2, x, resolve, reject) {
    if (promise2 === x) {
      // 如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise
      return reject(new TypeError('循环引用'))
    }
    if (x instanceof MyPromise) {
      // 如果 x 为 Promise ,则使 promise 接受 x 的状态
      // 如果 x 处于等待态, promise 需保持为等待态直至 x 被执行或拒绝
      queueMicrotask(() => {
        if (x.status === PENDING) {
          x.then((y) => {
            this.resolvePromise(promise2, y, resolve, reject)
          }, reject)
        } else {
          // 如果 x 处于执行态,用相同的值执行 promise
          // 如果 x 处于拒绝态,用相同的据因拒绝 promise
          x.then(resolve, reject)
        }
      })
    } else if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
      // 确保x为引用类型, 如果 x 为对象或者函数,
      // 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
      // 我们声明一个called防止多次调用
      let called = false
      try {
        // 把 x.then 赋值给 then
        let then = x.then
        if (typeof then === 'function') {
          // 如果 then 是函数,将 x 作为函数的作用域 this 调用之。传递两个回调函数作为参数,
          // 第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise:
          // then.call(x, resolvePromise, rejectPromise)
          // 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
          // 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
          then.call(
            x,
            (y) => {
              if (called) return
              called = true
              this.resolvePromise(promise2, y, resolve, reject)
            },
            (r) => {
              if (called) return
              called = true
              reject(r)
            }
          )
        } else {
          // 如果 then 不是函数,以 x 为参数执行 promise
          resolve(x)
        }
      } catch (e) {
        // 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
        // 如果调用 then 方法抛出了异常 e : 如果 resolvePromise 或 rejectPromise 已经被调用,则忽略之
        if (called) return
        called = true
        reject(e)
      }
    } else {
      // 如果 x 不为对象或者函数,以 x 为参数执行 promise
      resolve(x)
    }
  }
}
  • static表示class的静态方法,能够用类直接调用,如MyPromise.resolve

几个问题

为什么promise resolve了一个value, 最后输出的value值确是undefined

1
2
3
4
5
6
7
8
9
10
11
12
const test = new MPromise((resolve, reject) => {
    setTimeout(() => {
        resolve(111);
    }, 1000);
}).then((value) => {
    console.log('then');
});

setTimeout(() => {
    console.log(test);
}, 3000)

答: 因为现在这种写法, 相当于在.then里return undefined, 所以最后的value是undefined. 如果显式return一个值, 就不是undefined了;比如return value.

.then返回的是一个新Promise, 那么原来promise实现的时候, 用数组来存回调函数有什么意义?

这个问题提出的时候, 应该是有一个假定条件, 就是链式调用的时候.

这个时候, 每一个.then返回的都是一个新promise, 所以每次回调数组FULFILLED_CALLBACK_LIST都是空数组.

针对这种情况, 确实用数组来存储回调没意义, 完全可以就用一个变量来存储。

1
2
3
4
5
6
7
8
9
const test = new MPromise((resolve, reject) => {
    setTimeout(() => {
        resolve(111);
    }, 1000);
}).then((value) => {
    
}).then(() => {

})

但是还有一种promise使用的方式, 这种情况下, promise实例是同一个, 数组的存在就有了意义

1
2
3
4
5
6
7
8
9
10
const test = new MPromise((resolve, reject) => {
    setTimeout(() => {
        resolve(111);
    }, 1000);
})

test.then(() => {});
test.then(() => {});
test.then(() => {});
test.then(() => {});

为什么我在catch的回调里, 打印promise, 显示状态是pending

1
2
3
4
5
6
7
8
9
10
11
12
13
const test = new MPromise((resolve, reject) => {
    setTimeout(() => {
        reject(111);
    }, 1000);
}).catch((reason) => {
    console.log('报错' + reason);
    console.log(test)
});

setTimeout(() => {
    console.log(test);
}, 3000)

  1. catch 函数会返回一个新的promise, 而test就是这个新promise
  2. catch 的回调里, 打印promise的时候, 整个回调还并没有执行完成(所以此时的状态是pending), 只有当整个回调完成了, 才会更改状态
  3. catch 的回调函数, 如果成功执行完成了, 会改变这个新Promise的状态为fulfilled