手写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
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
const STATUS = { PENDING: 'PENDING', FUFILLED: 'FUFILLED', REJECTED: 'REJECTED' }


// 我们的promise 按照规范来写 就可以和别人的promise公用
function resolvePromise(x, promise2, resolve, reject) {
// If promise and x refer to the same object, reject promise with a TypeError as the reason.
if (promise2 == x) { // 防止自己等待自己完成
return reject(new TypeError('类型错了'))
}
// 看x 是普通值还是promise 如果是promise要采用他的状态
if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
// x可以是一个对象 或者是函数
let called;
try {
let then = x.then; // 就看一下这个对象是否有then方法
if (typeof then == 'function') {
// then是函数 我就认为这个x是一个promise
// 如果x是promise 那么就采用他的状态
then.call(x, function(y) { // 调用返回的promise 用他的结果 作为下一次then的结果
if (called) return
called = true;
// 递归解析成功后的值 直到他是一个普通值为止
resolvePromise(y, promise2, resolve, reject);
}, function(r) {
if (called) return
called = true;
reject(r);
})
} else {
resolve(x); // 此时x 就是一个普通对象
}
} catch (e) {
if (called) return
called = true;
reject(e); // 取then时抛出错误了
}
} else {
resolve(x); // x是一个原始数据类型 不能是promise
}
// 不是proimise 直接就调用resolve
}
class Promise {
constructor(executor) {
this.status = STATUS.PENDING;
this.value = undefined;
this.reason = undefined;
this.onResolvedCallbacks = []; // 存放成功的回调的
this.onRejectedCallbacks = []; // 存放失败的回调的
const resolve = (val) => {

if(val instanceof Promise){ // 是promise 就继续递归解析
return val.then(resolve,reject)
}

if (this.status == STATUS.PENDING) {
this.status = STATUS.FUFILLED;
this.value = val;
// 发布
this.onResolvedCallbacks.forEach(fn => fn());
}
}
const reject = (reason) => {
if (this.status == STATUS.PENDING) {
this.status = STATUS.REJECTED;
this.reason = reason;
// 腹部
this.onRejectedCallbacks.forEach(fn => fn());
}
}
try {
executor(resolve, reject);
} catch (e) {
// 出错走失败逻辑
reject(e)
}
}
then(onFulfilled, onRejected) { // swtich 作用域
// 可选参数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : x => x
onRejected = typeof onRejected === 'function'? onRejected: err=> {throw err}
let promise2 = new Promise((resolve, reject) => {
if (this.status === STATUS.FUFILLED) {
// to....
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(x, promise2, resolve, reject)
} catch (e) {
reject(e);
}
}, 0);
}
if (this.status === STATUS.REJECTED) {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(x, promise2, resolve, reject)
} catch (e) {
reject(e);
}
}, 0);
}
if (this.status === STATUS.PENDING) {
// 装饰模式 切片编程
this.onResolvedCallbacks.push(() => { // todo..
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(x, promise2, resolve, reject)
} catch (e) {
reject(e);
}
}, 0);
})
this.onRejectedCallbacks.push(() => { // todo..
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(x, promise2, resolve, reject)
} catch (e) {
reject(e);
}
}, 0);

})
}
});
return promise2;
}
catch(err){ // 默认没有成功 只有失败
return this.then(null,err)
}
static resolve(val){
return new Promise((resolve,reject)=>{
resolve(val);
})
}
static reject(reason){ // 失败的promise
return new Promise((resolve,reject)=>{
reject(reason);
})
}
}
// 测试时会调用此方法 Promise.finally resolve reject catch all
Promise.defer = Promise.deferred = function () {
let dfd = {};
dfd.promise = new Promise((resolve,reject)=>{
dfd.resolve = resolve;
dfd.reject = reject
})
return dfd;
}

// npm install promises-aplus-tests -g
module.exports = Promise;
-------------本文结束感谢您的阅读-------------

本文标题:手写promise

文章作者:Water

发布时间:2020年09月15日 - 15:09

最后更新:2023年08月01日 - 06:08

原始链接:https://water.buging.cn/2020/09/15/手写promise/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

坚持原创技术分享,您的支持将鼓励我继续创作!