自己实现一个promise

自己实现一个promise,符合A+规范,具有resovle, reject, then, throw, all, race 等方法

Promise 对象用于表示一个异步操作的最终状态(完成或失败),以及其返回的值。是异步编程的一种解决方案。

它有三种状态,pending | fulfilled | rejected,状态变更后不可逆,有个then方法,为 Promise 实例添加状态改变时的回调函数

调用方式

1
2
3
4
5
6
7
8
9
10
var promise1 = new Promise(function(resolve, reject) {
setTimeout(resolve, 100, 'foo');
});
promise1.then(function(value) {
// success
}, function(error) {
// failure
});
console.log(promise1);

大体框架

Promise的方法:

  • Promise.all()
  • Promise.prototype.catch()
  • Promise.prototype.finally()
  • Promise.prototype.then()
  • Promise.race()
  • Promise.reject()
  • Promise.resolve()

可以想到Promise大体可能是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Promise {
constructor(executor) {
}
catch() {
}
finally() {
}
then(onFulfilled, onRejected) {
}
}
Promise.all = () => {}
Promise.race = () => {}
Promise.reject = () => {}
Promise.resolve = () => {}

构造函数实现

  1. 需要记录状态,回调的执行会参考这个变量
  2. 需要resolve和reject去置状态, 并按次序执行回调函数
  3. 需要队列去存储回调函数,因为定义的时候可能还没到执行的时机
  4. fulfilled的回调函数调用时需要传入value
  5. rejected的回调函数调用时需要传入reason
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
constructor(executor) {
this.status = 'pending'
this.successCks = []
this.failCks = []
this.value = undefined
this.reason = undefined
let resolve = (value) => {
if (this.status === 'pending') {
this.status = 'fulfilled'
this.value = value
let len = this.successCks.length
while(len--) {
let fn = this.successCks.shift()
fn(value)
}
}
}
let reject = (reason) => {
if (this.status === 'pending') {
this.status = 'rejected'
this.reason = reason
let len = this.failCks.length
while (len--) {
let fn = this.failCks.shift()
fn(reason)
}
}
}
executor(resolve, reject)
}

then的实现

到上一步,我们都没法验证resolve是否达到我们的预期,因为成功的回调函数队列根本就是空的,我们来写下then,来指定一下我们的回调函数,这里注意指定回调函数时,如果promise的状态已经是fullfilled状态,需要直接执行回调函数,参数使用的是之前resolve传入的参数value

代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
then(onFulfilled, onRejected) {
if (this.status === 'pending') {
if (typeof onFulfilled === 'function') {
this.successCks.push(onFulfilled)
}
if (typeof onRejected === 'function') {
this.failCks.push(onRejected)
}
} else if (this.status === 'fulfilled') {
if (typeof onFulfilled === 'function') {
// 它必须在fulfilled 后调用,且`value`为其第一个参数。
onFulfilled(this.value)
}
} else if (this.status == 'rejected') {
if (typeof onRejected === 'function') {
// 它必须在rejected后调用, 且`reason`为其第一个参数。
onRejected(this.reason)
}
}
}
小测一把
1
2
3
4
5
6
7
8
9
10
11
12
13
14
let promise = new Promise(function(resolve, reject) {
console.log('Promise');
resolve();
});
promise.then(function() {
console.log('resolved.');
});
console.log('Hi!');
// Promise
// resolved
// Hi

看到时序上有问题,但是then添加的方法确实执行了,算是成功了部分

链式调用

我们知道promise的一大功能是解决回调地狱问题,用链式来替代嵌套,所以链式调用是重点

Q: 那是不是直接在then方法里直接返回当前的promise实例就是我们想要的链式调用呢?

A: 不是的,它返回的是一个新的promise实例。

Q: why?
A: 想想实际的需求吧,我们可以要在一个异步操作之后,在它的回调函数里,又执行一个新的异步操作,我们链式调用的回调函数其实是想针对新的这个异步操作来说的。

Q: 新的实例的状态和哪个实例保持一致?

A: 如果前一个回调返回值x是一个promise实例,那链式调用返回的实例与x保持相同的状态,如果不是,那新的promise实例的状态与当前promise实例保持一致

resolveHandler 实现

鉴于对于x的类型会进行不同处理,我们不妨写一个方法专门处理一下

  1. 如果promise 和 x 指向相同的值, 使用 TypeError做为原因将promise拒绝。
  2. 如果 x 是一个promise, 采用其状态:
    • 如果x是pending状态,promise必须保持pending走到x fulfilled或rejected.
    • 如果x是fulfilled状态,将x的value用于fulfill promise
    • 如果x是rejected状态, 将x的reason用于reject promise
  3. 如果x是一个对象或一个函数:
    • 将 then 赋为 x.then.
    • 如果在取x.then值时抛出了异常,则以这个异常做为原因将promise拒绝。
    • 如果 then 是一个函数, 以x为this调用then函数, 且第一个参数是resolvePromise,第二个参数是rejectPromise,且:
      • 当 resolvePromise 被以 y为参数调用, 执行 [Resolve].
      • 当 rejectPromise 被以 r 为参数调用, 则以r为原因将promise拒绝。
      • 如果 resolvePromise 和 rejectPromise 都被调用了,或者被调用了多次,则只第一次有效,后面的忽略。
      • 如果在调用then时抛出了异常,则:
        • 如果 resolvePromise 或 rejectPromise 已经被调用了,则忽略它。
        • 否则, 以e为reason将 promise 拒绝。
    • 如果 then不是一个函数,则 以x为值fulfill promise。
  4. 如果 x 不是对象也不是函数,则以x为值 fulfill promise。

按照上述的要求,我们一步步处理一下

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
let resolveHandler = (promise, x, resolve, reject) => {
// 第一条
if (x === promise) {
return reject(new TypeError('不能是自身'))
}
// 第二条
if (x instanceof Promise) {
if (x.status === 'fulfilled') {
return resolve(x.value)
}
if (x.status === 'rejected') {
return reject(x.reason)
}
if (x.status === 'pending') {
return x.then(resolve, reject)
}
}
// 第三条
if (typeof x === 'object' || typeof x === 'function') {
let then
try {
then = x.then
} catch (e) {
return reject(e)
}
if (typeof then === 'function') {
let called = false
try {
then.call(x, y => {
// 只能调用一次
if (!called) {
called = true
resolveHandler(promise2, y, resolve, reject)
}
}, r => {
if (!called) {
called = true
reject(r)
}
})
} catch (e) {
if (!called) {
reject(e)
}
}
return
}
}
resolve(x)
}

then的链式实现
  • 如果onFulfilled 或 onRejected 返回了值x, 则执行Promise 解析流程[[Resolve]](promise2, x).
  • 如果onFulfilledonRejected抛出了异常e, 则promise2应当以e为reason被拒绝。
  • 如果 onFulfilled 不是一个函数且promise1已经fulfilled,则promise2必须以promise1的值fulfilled.
  • 如果 onReject 不是一个函数且promise1已经rejected, 则promise2必须以相同的reason被拒绝.
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
then(onFulfilled, onRejected) {
let promise2
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err }
if (this.status === 'pending') {
// 返回新的实例
promise2 = new Promise((resolve, reject) => {
this.successCks.push((val) => {
try {
let ret = onFulfilled(val)
resolveHandler(promise2, ret, resolve, reject)
} catch (e) {
reject(e)
}
})
this.failCks.push((val) => {
try {
let ret = onRejected(val)
resolveHandler(promise2, ret, resolve, reject)
} catch (e) {
reject(e)
}
})
})
return promise2
}
if (this.status === 'fulfilled') {
promise2 = new Promise((resolve, reject) => {
try {
let ret = onFulfilled(this.value)
resolveHandler(promise2, ret, resolve, reject)
} catch (e) {
reject(e)
}
})
return promise2
}
if (this.status === 'rejected') {
promise2 = new Promise((resolve, reject) => {
try {
let ret = onRejected(this.reason)
resolveHandler(promise2, ret, resolve, reject)
} catch (e) {
reject(e)
}
})
return promise2
}
}

到这一步,不考虑时序的问题,上述和规范上对起来差不多了,再看看其他的方法

Promise.prototype.catch 实现

rejected时触发

1
2
3
catch (onRejected) {
return this.then(undefined, onRejected)
}

Promise.prototype.finally 实现

1
2
3
finally(onFinally) {
return this.then(onFinally, onFinally)
}

Promise.resolve 实现

方法返回一个以给定值解析后的Promise对象。但如果这个值是个thenable(即带有then方法),返回的promise会“跟随”这个thenable的对象,采用它的最终状态(指resolved/rejected/pending/settled);如果传入的value本身就是promise对象,则该对象作为Promise.resolve方法的返回值返回;否则以该值为成功状态返回promise对象。

1
2
3
4
5
6
7
8
9
10
Promise.resolve = function(value) {
let promise
if (value instanceof Promise) {
return value
}
promise = new Promise((resolve, reject) => {
resolveHandler(promise, value, resolve, reject)
});
return promise
}

Promise.reject 实现

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

Promise.all 和 Promise.race 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Promise.race = function(arr) {
return new Promise((resolve, reject) => {
arr.forEach(item => {
item.then(resolve, reject)
})
})
}
Promise.all = function(arr) {
let set = new Set(arr)
return new Promise((resolve, reject) => {
arr.forEach(item => {
item.then(y => {
set.delete(item)
if (set.size == 0) {
resolve()
}
}, r => {
reject(r)
})
})
})
}

参考资料: