Iwen's blog Iwen's blog
首页
  • 前端文章

    • JavaScript
    • Vue
  • 学习笔记

    • 《JavaScript教程》笔记
    • 《JavaScript高级程序设计》笔记
    • 《ES6 教程》笔记
    • 《Vue》笔记
    • 《TypeScript 从零实现 axios》
    • 小程序笔记
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • Linux
  • 学习
  • 面试
  • 心情杂货
  • 友情链接
  • 网站
  • 资源
  • Vue资源
  • 分类
  • 标签
  • 归档
复盘
关于

Iwen

不摸鱼的哥哥
首页
  • 前端文章

    • JavaScript
    • Vue
  • 学习笔记

    • 《JavaScript教程》笔记
    • 《JavaScript高级程序设计》笔记
    • 《ES6 教程》笔记
    • 《Vue》笔记
    • 《TypeScript 从零实现 axios》
    • 小程序笔记
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • Linux
  • 学习
  • 面试
  • 心情杂货
  • 友情链接
  • 网站
  • 资源
  • Vue资源
  • 分类
  • 标签
  • 归档
复盘
关于
  • Vue

  • Vue进阶

  • CSS

  • ES6

  • Base

  • Core

  • Array

  • Object

  • String

  • Async

  • Browser

  • Http

  • 性能优化

  • 正则

  • 经典总结

  • 设计模式

  • 数据结构

  • 算法

  • 手写

    • call、apply
    • 实现bind函数
    • instanceOf
    • new 原理及模拟实现
    • 防抖、节流
    • 发布订阅模式
    • 深拷贝
    • Promise A+
      • 简易实现
      • 回调数组
      • 链式调用
      • 错误捕获
      • Promise/A+
      • 参考
    • Promise 进阶
    • Array-prototype-map
    • Array-prototype-filter
    • Array-prototype-reduce
    • 并发请求
    • 继承
    • JSON 字符串
  • TypeScript

  • 复盘
  • 手写
Mr.w
2020-11-29

Promise A+

# Promise A+

# 简易实现

//定义三种状态
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

function MyPromise(executor){
    const self = this;          // 缓存当前promise实例
    self.state = PENDING;       // 默认状态
    self.value = null;          // 成功的值
    self.error = null;          // 失败的值
    self.onFulfilled = null;    // 保存then中的回调
    self.onRejected = null;     // 保存catch中的回调

    const resolve = (value) => {
        if(self.state == PENDING){
            setTimeout(() => {
                self.state = FULFILLED;
                self.value = value;
                self.onFulfilled(self.value)
            })
        }
    }
    
    const reject = (error) => {
        if(self.state == PENDING){
            setTimeout(() => {
                self.state = REJECTED;
                self.error = error;
                self.onRejected(self.value)
            })
        }
    }

    // 如果executor执行报错,直接执行reject
    try{
        executor(resolve, reject)
    }catch(err){
        reject(err)
    }
}

MyPromise.prototype.then = function(onFulfilled, onRejected){
    if(this.state == PENDING){
        this.onFulfilled = onFulfilled
        this.onRejected = onRejected
    }
    else if(this.state == FULFILLED){
        onFulfilled(this.value)
    }
    else{
        onRejected(this.error)
    }
    return this;
}

//执行promise
new MyPromise((resolve, reject) => {
    setTimeout(() => {
        resolve(1)
    }, 1000)
}).then(res => {
    console.log(res) // 1
})
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

可以看到,Promise 的本质是一个有限状态机,存在三种状态:

  • PENDING(等待)
  • FULFILLED(成功)
  • REJECTED(失败)

对于 Promise 而言,状态的改变不可逆,即由等待态变为其他的状态后,就无法再改变了。

# 回调数组

首先只能执行一个回调函数,对于多个回调的绑定就无能为力,比如下面这样:

let promise = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        resolve(1)
    })
});

let x1 = promise.then(res => {
    console.log("第一次展示", res);    
})

let x2 = promise.then(res => {
    console.log("第二次展示", res);    
})

// 只会执行最后一个, 前面的会被覆盖
let x3 = promise.then(res => {
    console.log("第三次展示", res);    
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

这里我绑定了三个回调,想要在 resolve() 之后一起执行,那怎么办呢?

需要将 onFulfilled 和 onRejected 改为数组,调用 resolve 时将其中的方法拿出来一一执行即可。

self.onFulfilledCallback = [];
self.onRejectedCallback = [];
1
2
MyPromise.prototype.then = function(onFulfilled, onRejected){
    // 判断then的参数是否为函数类型,不是则赋予默认值
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
    onRejected = typeof onRejected === 'function' ? onRejected : error => { throw error }

    if(this.state == PENDING){
        this.onFulfilledCallbacks.push(onFulfilled);
        this.onRejectedCallbacks.push(onRejected);
    }
    else if(this.state == FULFILLED){
        onFulfilled(this.value)
    }
    else{
        onRejected(this.error)
    }
    return this;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

接下来将 resolve 和 reject 方法中执行回调的部分进行修改

遍历调用所有方法:

// resolve
self.onFulfilledCallbacks.map(cd => cd(self.value));
// reject
self.onRejectedCallbacks.map(cd => cd(self.error));
1
2
3
4

# 链式调用

我们采用目前的代码来进行测试:

const promise = (name) => {
    return new MyPromise((resolve, reject) => {
        resolve(name)
    })
}

promise('1111').then(res => {
    console.log(res)
    return promise('22222')
}).then(res => {
    console.log(res)
})
// 1111
// 1111
1
2
3
4
5
6
7
8
9
10
11
12
13
14

咦?怎么打印了两个 1111,第二次不是 2222 吗?

问题出在这:

MyPromise.prototype.then = function(onFulfilled, onRejected) {
  //...
  return this;
}
1
2
3
4

这么写每次返回的都是第一个 Promise。then 函数当中返回的第二个 Promise 直接被无视了!

说明 then 当中的实现还需要改进, 我们现在需要对 then 中返回值重视起来。

先对返回Promise的情况进行处理:

function resolvePromise(bridgePromise, x, resolve, reject) {
  //如果x是一个promise
  if (x instanceof MyPromise) {
    // 拆解这个 promise ,直到返回值不为 promise 为止
    if (x.status === PENDING) {
      x.then(y => {
        resolvePromise(bridgePromise, y, resolve, reject);
      }, error => {
        reject(error);
      });
    } else {
      x.then(resolve, reject);
    }
  } else {
    // 非 Promise 的话直接 resolve 即可
    resolve(x);
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

然后,对所有状态进行处理

if(this.state == PENDING){
    return bridgePromise = new MyPromise((resolve, reject) => {
        // 避免传入错误值
        self.onFulfilledCallbacks.push((value) => {
            try{
                const x = onFulfilled(value)
                resolvePromise(bridgePromise, x, resolve, reject);
            }catch(err){
                reject(err)
            }
        })
        self.onRejectedCallbacks.push((err) => {
            try{
                const x = onRejected(err)
                resolvePromise(bridgePromise, x, resolve, reject);
            }catch(err){
                reject(err)
            }
        })
    })
}
else if(this.state == FULFILLED){
    return bridgePromise = new MyPromise((resolve, reject) => {
        setTimeout(() => {
            try{
                const x = onFulfilled(self.value)
                resolvePromise(bridgePromise, x, resolve, reject);
            }catch(err){
                reject(err)
            }
        })
    })
}
else{
    return bridgePromise = new MyPromise((resolve, reject) => {
        setTimeout(() => {
            try{
                const x = onRejected(self.error)
                resolvePromise(bridgePromise, x, resolve, reject);
            }catch(err){
                reject(err)
            }
        })
    })
}
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

# 错误捕获

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

// then 的实现中
onRejected = typeof onRejected === "function" ? onRejected : error => { throw error };
1
2
3
4
5
6

对,就是这么几行,catch 原本就是 then 方法的语法糖。

到这里就ok了, 接下来把完整代码贴出来

# Promise/A+

//定义三种状态
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

function MyPromise(executor){
    const self = this;          // 缓存当前promise实例
    self.state = PENDING;       // 默认状态
    self.value = null;          // 成功的值
    self.error = null;          // 失败的值
    self.onFulfilledCallbacks = [];    // 保存then中的回调
    self.onRejectedCallbacks = [];     // 保存catch中的回调

    const resolve = (value) => {
        if(self.state == PENDING){
            setTimeout(() => {
                self.state = FULFILLED;
                self.value = value;
                self.onFulfilledCallbacks.map(cd => cd(self.value));
            })
        }
    }
    const reject = (error) => {
        if(self.state == PENDING){
            setTimeout(() => {
                self.state = REJECTED;
                self.error = error;
                self.onRejectedCallbacks.map(cd => cd(self.error));
            })
        }
    }

    // 如果executor执行报错,直接执行reject
    try{
        executor(resolve, reject)
    }catch(err){
        reject(err)
    }
}

MyPromise.prototype.then = function(onFulfilled, onRejected){
    let self = this,
        bridgePromise;

    // 判断then的参数是否为函数类型,不是则赋予默认值
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
    onRejected = typeof onRejected === 'function' ? onRejected : error => { throw error }

    function resolvePromise(bridgePromise, x, resolve, reject) {
        // 如果x是一个promise
        if(x instanceof MyPromise){
            if(x.state === PENDING){
                x.then(y => {
                    resolvePromise(bridgePromise, y, resolve, reject)
                }), err => {
                    reject(err)
                }
            }else{
                x.then(resolve, reject)
            }
        }else{
            // 非 Promise 的话直接 resolve 即可
            resolve(x)
        }
    }

    if(this.state == PENDING){
        return bridgePromise = new MyPromise((resolve, reject) => {
            // 避免传入错误值
            self.onFulfilledCallbacks.push((value) => {
                try{
                    const x = onFulfilled(value)
                    resolvePromise(bridgePromise, x, resolve, reject);
                }catch(err){
                    reject(err)
                }
            })
            self.onRejectedCallbacks.push((err) => {
                try{
                    const x = onRejected(err)
                    resolvePromise(bridgePromise, x, resolve, reject);
                }catch(err){
                    reject(err)
                }
            })
        })
    }
    else if(this.state == FULFILLED){
        return bridgePromise = new MyPromise((resolve, reject) => {
            setTimeout(() => {
                try{
                    const x = onFulfilled(self.value)
                    resolvePromise(bridgePromise, x, resolve, reject);
                }catch(err){
                    reject(err)
                }
            })
        })
    }
    else{
        return bridgePromise = new MyPromise((resolve, reject) => {
            setTimeout(() => {
                try{
                    const x = onRejected(self.error)
                    resolvePromise(bridgePromise, x, resolve, reject);
                }catch(err){
                    reject(err)
                }
            })
        })
    }
}

MyPromise.prototype.catch = function(onRejected) {
    return this.then(null, onRejected)
}
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
const promise = (name) => {
    return new MyPromise((resolve, reject) => {
        resolve(name)
    })
}
promise('1111').then(res => {
    console.log(res)
    return promise('22222')
}).then(res => {
    console.log(res)
})
new MyPromise((resolve, reject) => {
    resolve('222')
}).then(res => {
    console.log(res)
})
// 1111
// 222
// 22222
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 参考

  • 可能是目前最易理解的手写promise (opens new window)
  • 史上最易读懂的 Promise/A+ 完全实现 (opens new window)
深拷贝
Promise 进阶

← 深拷贝 Promise 进阶→

最近更新
01
flex布局页面自适应滚动条问题
12-28
02
前后端分离开发请求接口跨域如何携带cookie的问题
12-28
03
怎么实现div长宽比固定width-height4比3
12-28
更多文章>
Theme by Vdoing | Copyright © 2017-2022 Iwen | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式