ES6 Promise 的不完全实现

一、Promise/A+ 规范

1.Promise存在三个状态:pending(等待态)、fulfilled(成功态)、rejected(失败态);
2.pending为初始态,并可以转化为fulfilled和rejected;
3.成功时,不可转为其他状态,且必须有一个不可改变的值 (value);
4.失败时,不可转为其他状态,且必须有一个不可改变的原因 (reason);
5.new Promise(executor = (resolve, reject) => {resolve(value)}), resolve(value)将状态置为 fulfilled;
6.new Promise(executor = (resolve, reject) => {reject(reason)}), reject(reason)将状态置为 rejected;
7.如果 executor 执行异常也会 reject();
8.thenable: then(onFulfilled, onRejected?);
  8.1 onFulfilled: status 为 fulfilled,执行 onFulfilled, 传入 value
  8.2 onRejected: status 为 rejected, 执行 onRejected

二、同步 Promise

同步 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
51
52
53
54
55
56
57
58
59
60
61
62
63
// 1.Promise存在三个状态:pending(等待态)、fulfilled(成功态)、rejected(失败态)
const STATUS_PENDING = ‘pending‘
const STATUS_FULFILLED = ‘fulfilled‘
const STATUS_REJECTED = ‘rejected‘
class myPromise {
    constructor(executor) {
        // pending为初始态,并可以转化为fulfilled和rejected
        this.status = STATUS_PENDING
        this.value = ‘‘ // 3
        this.reason = ‘‘ // 4
 
        let resolve = value => {
            // 5.
            if (this.status === STATUS_PENDING) {
                this.status = STATUS_FULFILLED
                this.value = value
            }
        }
        let reject = reason => {
            //6.
            if (this.status === STATUS_PENDING) {
                this.status = STATUS_REJECTED
                this.reason = reason
            }
        }
        // 7.
        try {
            executor(resolve, reject);
        catch (err) {
            reject(err);
        }
    }
  // 8.
    then(onFulfilled = () => {}, onRejected = () => {}) {
        // 8.1
        if (this.status === STATUS_FULFILLED) {
            onFulfilled(this.value)
        }
        // 8.2
        if (this.status === STATUS_REJECTED) {
            onRejected(this.reason)
        }
    }
}
 
 
let ps = new myPromise(resolve => {
    console.log(‘before resolve‘)
    resolve(1)
})
ps.then(res => {
    console.log(res)
})
 
let pe = new myPromise((resolve, reject) => {
    console.log(‘before reject‘)
    reject(‘reject error‘)
})
pe.then(res => {
    console.log(res)
}, error => {
    console.log(error)
})

在上面的两个例子中,所有任务的执行都是同步的,可以与接下来的异步 promise 的执行顺序对比下。

三、异步 Promise

先放下我们的运行实例:

1
2
3
4
5
6
7
8
9
let pa = new myPromise(resolve => {
    console.log(‘before resolve‘)
    setTimeout(()=>{
        resolve(1)
    },1000)
})
pa.then(res => {
    console.log(res)
})

 在这里,我们的 executor 函数体中有异步的代码块,那么在执行 executor(resolve, reject); 的时候,setTimeout中的任务就会被推入异步执行栈中,等待主线程中的宏任务(详见 EventLoop in Js)全部计算完成再执行这个任务,因此,我们打断点会发现,  then() 的执行会早于  resolve(1) , 而在 then() 执行的时候,pa.status 依然是 pending,接下啦根据逻辑判断 then 函数执行完成退出,然后执行异步任务,整个代码执行完毕,故控制台只会打印出 ‘before resolve‘。
明白了这里的执行顺序,我们即可以进行完善,代码如下:(标记 改 A)

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
const STATUE_PENDING = ‘pending‘;
const STATUE_FULFILLED = ‘fulfilled‘;
const STATUS_REJECTED = ‘rejected‘;
 
class myPromise {
    constructor(executor) {
        // pending 是初始态,并可以转化成 fulfilled 和 rejected
        this.status = STATUE_PENDING;
        this.value = ‘‘// 3.
        this.reason = ‘‘// 4.
 
        // 改 A start
        // 存放成功的数组
        this.onResolvedCallbacks = [];
        // 存放失败的数组
        this.onRejectedCallbacks = [];
        // 改 A end
 
        let resolve = value => {
            // 5.
            if (this.status === STATUE_PENDING) {
                this.status = STATUE_FULFILLED;
                this.value = value;
 
                // 改 A start
                // 成功之后的执行栈,
                this.onResolvedCallbacks.forEach(fn => fn());
                // 改 A end
            }
        }
         
        let reject = reason =>  {
            // 6.
            if (this.status === STATUE_PENDING) {
                this.status = STATUS_REJECTED;
                this.reason = reason;
 
                // 改 A start
                // 成功之后的执行栈,
                this.onRejectedCallbacks.forEach(fn => fn());
                // 改 A end
            }
        }
 
 
        // 7.
        try {
            executor(resolve, reject);
        catch (error) {
            reject(err);
        }
    }
 
    // 8
    then(onFulfilled = () => {}, onRejected = () => {}){
        // 8.1
        if (this.status === STATUE_FULFILLED) onFulfilled(this.value);
        // 8.2
        if (this.status === STATUS_REJECTED) onRejected(this.reason);
 
        // 改 A start
        // 如果是处于异步任务的
        if (this.status === STATUE_PENDING) {
            // 推入相应的执行栈
            this.onResolvedCallbacks.push(() => onFulfilled(this.value));
            this.onRejectedCallbacks.push(() => onRejected(this.reason));
        }
        // 改 A end
    }
}

为什么要分析下这边的执行顺序,一来复习下 EventLoop,二来对下面的链式调用的理解比较重要(这里也是打断点才发现的,推翻了一直以来对 Promise.then 的理解,之前一直认为 executor 中的异步任务执行完了才真正的去执行 then 函数和里面的onFulfilled/onRejected 函数)。

四、new Promise().then().then()... 链式调用

Promise 的链式调用和 jquery 的链式调用是不同的,在 jquery 或者一些其他的三方包中,我们在函数末尾加上 returnthis  即可实现,所以这里无论链多少,都是在同一个对象上做文章。而 Promise 的每一次(链式)调用,其都会产生一个新的 Promise 对象, 并基于这个新的 Promise 调用 then 函数,虽然我们写的时候是.then().then()...。 首先我们来看实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let pc = new myPromise((resolve, reject) => {
    console.log(0);
    setTimeout(() => {
        resolve(1);
    }, 3000);
})
pc.then(res => {
    console.log(res);
 
    return new myPromise(resolve => {
        console.log(2);
        setTimeout(() => {
            resolve(3)
        }, 3000);
    })
}).then(res => {
    console.log(res);
})

注意下结构,我们在第一个 then 的参数函数中会有一个新的 Promise 返回。
然后是 MyPromise 类:

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
const STATUE_PENDING = ‘pending‘;
const STATUE_FULFILLED = ‘fulfilled‘;
const STATUS_REJECTED = ‘rejected‘;
 
function resolvePromise(promise2, x, resolve, reject) {
    // 处理循环引用报错
    if (x === promise2) {
        // reject 报错
        return reject(new TypeError(‘chaining cycle detected for promise‘));
    }
 
    // 记录, 防止多次调用
    let called;
 
    // x 是对象(不包括 null)或者函数
    if (x != null && (typeof x === ‘object‘ || typeof x === ‘function‘)) {
        try {
            // A+ 规定,声明 then = x 的 then 方法
            let then = x.then;
            // then 是 function,则默认是 promise
            if (typeof then === ‘function‘) {
                // 就让 then 执行, 第一个参数是 this, 后面是成功的回调和失败的回调
 
                then.call(x, y => {
                    // 成功和失败只能调用一个
                    if (called) return;
                    called = true;
 
                    // resolve 的结果依旧是promise 那就继续解析
                    resolvePromise(promise2, y, resolve, reject);
                }, err => {
                    // 成功和失败只能调用一个
                    if (called) return;
                    called = true;
 
                    // 失败,停止继续调用
                    reject(err);
                })
            else {// 不是的话直接 resolve 即可
                resolve(x);
            }
 
        catch (error) {
            // 出错,即失败
            if (called) return;
            called = true;
 
            // 取 then 出错了那就不继续了
            reject(error);
        }
    else {
        resolve(x);
    }
}
 
class myPromise {
    constructor(executor) {
        this.status = STATUE_PENDING;
        this.value = ‘‘;
        this.reason = ‘‘;
 
 
        this.onResolvedCallbacks = [];
        this.onRejectedCallbacks = [];
 
        let resolve = value => {
            if (this.status === STATUE_PENDING) {
                this.status = STATUE_FULFILLED;
                this.value = value;
 
                this.onResolvedCallbacks.forEach(fn => fn());
            }
        }
         
        let reject = reason =>  {
            if (this.status === STATUE_PENDING) {
                this.status = STATUS_REJECTED;
                this.reason = reason;
 
                this.onRejectedCallbacks.forEach(fn => fn());
            }
        }
 
        try {
            executor(resolve, reject);
        catch (error) {
            reject(err);
        }
    }
 
    then(onFulfilled, onRejected){
        // onFulfilled 不是函数, 则忽略,直接返回 value
        onFulfilled = typeof onFulfilled === ‘function‘ ? onFulfilled : value => value;
        // onRejected 不是函数, 则忽略,直接扔出错误
        onRejected = typeof onRejected === ‘function‘ ? onRejected : err => {
            throw err;
        };
 
        let promise2 = new myPromise((resolve, reject) => {
            if (this.status === STATUE_FULFILLED) {
                // 推一个异步任务
                setTimeout(() => {
                    try {
                        let x = onFulfilled(this.value);
                        resolvePromise(promise2, x, resolve, reject);   
                    catch (error) {
                        reject(error)
                    }
                }, 0);
            }
 
            if (this.status === STATUS_REJECTED) {
                // 推一个异步任务
                setTimeout(() => {
                    try {
                        let x = onRejected(this.reason);
                        resolvePromise(promise2, x, resolve, reject);   
                    catch (error) {
                        reject(error)
                    }
                }, 0);
            }
 
            if (this.status === STATUE_PENDING) {
                // 推到执行栈中
                this.onResolvedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onFulfilled(this.value);
                            resolvePromise(promise2, x, resolve, reject);   
                        catch (error) {
                            reject(error)
                        }
                    }, 0);
                });
 
                this.onRejectedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onRejected(this.reason);
                            resolvePromise(promise2, x, resolve, reject);   
                        catch (error) {
                            reject(error)
                        }
                    }, 0);
                })
            }
 
        });
 
        return promise2;
    }
}

这里要注意的是 then 函数的执行会产生一个新的 Promise, 第一个 then 函数的参数函数的执行也会产生一个新的 Promise。

五、其他:catch、resolve、reject、race和all

这里除 catch 外其余均是静态方法:

 1. catch(特殊的 then 方法):

1
2
3
catch(fn){
    return this.then(null,fn)
}

 2.reslove(resolve 一个值)

1
MyPromise.resolve = val => new Promise(resolve=> resolve(val))

 3.reject(reject 一个值)

1
MyPromise.reject = val => new Promise((resolve,reject)=> reject(val))

 4.race Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。

1
2
3
4
5
MyPromise.race = promises => {
    return new MyPromise((resolve, reject) =>
        promises.forEach(pro => pro.then(resolve, reject))
    )
}

 5.all Promise.all可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被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
MyPromise.all = function (promises) {
    return new Promise((resolve, reject) => {
        let index = 0;
        let result = [];
        if (promises.length === 0) {
            resolve(result);
        else {
            function processValue(i, data) {
                result[i] = data;
                if (++index === promises.length) {
                    resolve(result);
                }
            }
            for (let i = 0; i < promises.length; i++) {
                //promises[i] 可能是普通值
                Promise.resolve(promises[i]).then((data) => {
                    processValue(i, data);
                }, (err) => {
                    reject(err);
                    return;
                });
            }
        }
    });
}