# Promise
# 解决问题
多个异步请求存在依赖关系,需要按照一定顺序来执行,之前的解决是回调函数,会导致回调地狱:
http(function() {
http(function() {
http(function() {
// 回调地狱
});
});
});
2
3
4
5
6
7
Promise 将嵌套调用改为链式调用,增加了可读性和可维护性:
const fs = require("fs");
function read(filename) {
return new Promise((resolve, reject) => {
fs.readFile(filename, "utf8", (err, data) => {
if (err) reject(err);
resolve(data);
});
});
}
// 链式调用
// 这里配置的文件存在就会显示文件内容
// 不存在就会显示错误信息
read("./test.txt").then(
data => {
console.log(data);
},
err => {
console.log(err);
}
);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 业界实现
- bluebird
- es6 promise
- Q
# 实现
基于 [Promise/A+] 规范
# 基础版
Promise 基本特征:
- 三种状态 [规范 Promise/A+ 2.1]
pendingfulfilledrejected- 初始状态是
pending
new Promise执行构造函数的时候executor立即执行executor有两个参数resolve reject
- 一个
value保存成功状态的值 可能是undefined thenable promise[规范 Promise/A+ 1.3] - 一个
reason保存失败状态的值 [规范 Promise/A+ 1.5] - 状态只能是
pending -> rejected或者pending -> fulfilled - 必须有一个
then方法,接收两个参数onFulfilled(成功回调)onRejected(失败回调) [规范 Promise/A+ 2.2] - 调用
then时- 成功 执行
onFulfilled参数是value - 失败 执行
onRejected参数是reason - 异常 将异常作为
reason传递给下一个then的失败回调
- 成功 执行
const PENDING = "PENDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";
class Promise {
constructor(executor) {
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
// 存放成功的回调
this.onResolvedCallbacks = [];
// 存放失败的回调
this.onRejectedCallbacks = [];
let resolve = value => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
// 依次将对应的函数执行
this.onResolvedCallbacks.forEach(fn => fn());
}
};
let reject = reason => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
// 依次将对应的函数执行
this.onRejectedCallbacks.forEach(fn => fn());
}
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onFulfilled, onRejected) {
if (this.status === FULFILLED) {
onFulfilled(this.value);
}
if (this.status === REJECTED) {
onRejected(this.reason);
}
if (this.status === PENDING) {
// 如果promise的状态是 pending,需要将 onFulfilled 和 onRejected 函数存放起来,等待状态确定后,再依次将对应的函数执行
this.onResolvedCallbacks.push(() => {
onFulfilled(this.value);
});
this.onRejectedCallbacks.push(() => {
onRejected(this.reason);
});
}
}
}
// 测试
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("成功");
}, 1000);
}).then(
data => {
console.log("success", data);
},
err => {
console.log("faild", err);
}
);
// 输出
// "success 成功"
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
# 链式调用和值穿透性
then的参数onFulfilled onRejected可以缺省,如果不是函数类型可以忽略,然后在下一次调用then可以获取之前返回的值 [规范 Promise/A+ 2.2.1、2.2.1.1、2.2.1.2]then可以多次调用,每次返回都是Promise对象 [规范 Promise/A+ 2.2.7]then执行- 返回普通值作为参数传递到下次
then的成功回调 - 抛出异常将异常作为参数传递到下次
then的失败回调 [规范 Promise/A+ 2.2.7.2] - 返回一个
Promise对象,就会等待执行完成,后序和Promise逻辑一致 [规范 Promise/A+ 2.2.7.3、2.2.7.4] - 如果出现循环引用,抛出异常将异常作为参数传递到下次
then的失败回调 [规范 Promise/A+ 2.3.1] - 如果
then的返回值x是一个promise,且x同时调用resolve函数和reject函数,则第一次调用优先,其他所有调用被忽略 [规范 Promise/A+ 2.3.3.3.3]
- 返回普通值作为参数传递到下次
const PENDING = "PENDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";
const resolvePromise = (promise2, x, resolve, reject) => {
// 自己等待自己完成是错误的实现,用一个类型错误,结束掉 promise Promise/A+ 2.3.1
if (promise2 === x) {
return reject(
new TypeError("Chaining cycle detected for promise #<Promise>")
);
}
// Promise/A+ 2.3.3.3.3 只能调用一次
let called;
// 后续的条件要严格判断 保证代码能和别的库一起使用
if ((typeof x === "object" && x != null) || typeof x === "function") {
try {
// 为了判断 resolve 过的就不用再 reject 了(比如 reject 和 resolve 同时调用的时候) Promise/A+ 2.3.3.1
let then = x.then;
if (typeof then === "function") {
// 不要写成 x.then,直接 then.call 就可以了 因为 x.then 会再次取值,Object.defineProperty Promise/A+ 2.3.3.3
then.call(
x,
y => {
// 根据 promise 的状态决定是成功还是失败
if (called) return;
called = true;
// 递归解析的过程(因为可能 promise 中还有 promise) Promise/A+ 2.3.3.3.1
resolvePromise(promise2, y, resolve, reject);
},
r => {
// 只要失败就失败 Promise/A+ 2.3.3.3.2
if (called) return;
called = true;
reject(r);
}
);
} else {
// 如果 x.then 是个普通值就直接返回 resolve 作为结果 Promise/A+ 2.3.3.4
resolve(x);
}
} catch (e) {
// Promise/A+ 2.3.3.2
if (called) return;
called = true;
reject(e);
}
} else {
// 如果 x 是个普通值就直接返回 resolve 作为结果 Promise/A+ 2.3.4
resolve(x);
}
};
class Promise {
constructor(executor) {
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
let resolve = value => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
this.onResolvedCallbacks.forEach(fn => fn());
}
};
let reject = reason => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn());
}
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onFulfilled, onRejected) {
//解决 onFufilled,onRejected 没有传值的问题
//Promise/A+ 2.2.1 / Promise/A+ 2.2.5 / Promise/A+ 2.2.7.3 / Promise/A+ 2.2.7.4
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : v => v;
//因为错误的值要让后面访问到,所以这里也要跑出个错误,不然会在之后 then 的 resolve 中捕获
onRejected =
typeof onRejected === "function"
? onRejected
: err => {
throw err;
};
// 每次调用 then 都返回一个新的 promise Promise/A+ 2.2.7
let promise2 = new Promise((resolve, reject) => {
if (this.status === FULFILLED) {
//Promise/A+ 2.2.2
//Promise/A+ 2.2.4 --- setTimeout
setTimeout(() => {
try {
//Promise/A+ 2.2.7.1
let x = onFulfilled(this.value);
// x可能是一个proimise
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
//Promise/A+ 2.2.7.2
reject(e);
}
}, 0);
}
if (this.status === REJECTED) {
//Promise/A+ 2.2.3
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
if (this.status === PENDING) {
this.onResolvedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
}
});
return promise2;
}
}
// 测试
const promise = new Promise((resolve, reject) => {
reject("失败");
})
.then()
.then()
.then(
data => {
console.log(data);
},
err => {
console.log("err", err);
}
);
// 输出
// "失败 err"
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
Promise/A+ 规范提供了一个专门的测试脚本,可以测试所编写的代码是否符合 Promise/A+ 的规范
npm install -g promises-aplus-tests
那么在对应的目录执行以下命令:
promises-aplus-tests promise.js
在实现代码中增加:
Promise.defer = Promise.deferred = function() {
let dfd = {};
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
};
2
3
4
5
6
7
8
由于原生的 Promise 是 V8 引擎提供的微任务,我们无法还原 V8 引擎的实现,所以这里使用 setTimeout 模拟异步,所以原生的是微任务,这里是宏任务。
如果你想实现 promise 的微任务,可以 mutationObserver 替代 seiTimeout 来实现微任务。
# API
上述的 promise 源码已经符合 Promise/A+ 的规范,但是原生的 Promise 还提供了一些其他方法,如:
- Promise.resolve()
- Promise.reject()
- Promise.prototype.catch()
- Promise.prototype.finally()
- Promise.all()
- Promise.race()
Promise.resolve
默认产生一个成功的 promise
static resolve(data){
return new Promise((resolve,reject)=>{
resolve(data);
})
}
let resolve = value => {
// ======新增逻辑======
// 如果 value 是一个promise,那我们的库中应该也要实现一个递归解析
if (value instanceof Promise) {
// 递归解析
return value.then(resolve, reject);
}
// ===================
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
this.onResolvedCallbacks.forEach(fn => fn());
}
};
// 测试
Promise.resolve(
new Promise((resolve, reject) => {
setTimeout(() => {
resolve("ok");
}, 3000);
})
)
.then(data => {
console.log(data, "success");
})
.catch(err => {
console.log(err, "error");
});
// 输出
// "ok success"
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
Promise.reject
默认产生一个失败的 promise,Promise.reject是直接将值变成错误结果
static reject(reason){
return new Promise((resolve,reject)=>{
reject(reason);
})
}
2
3
4
5
Promise.prototype.catch
Promise.prototype.catch 用来捕获 promise 的异常,就相当于一个没有成功的 then
Promise.prototype.catch = function(errCallback) {
return this.then(null, errCallback);
};
2
3
Promise.prototype.finally
finally 表示不是最终的意思,而是无论如何都会执行的意思。 如果返回一个 promise 会等待这个 promise 也执行完毕。如果返回的是成功的 promise,会采用上一次的结果;如果返回的是失败的 promise,会用这个失败的结果,传到 catch 中。
romise.prototype.finally = function(callback) {
return this.then(
value => {
return Promise.resolve(callback()).then(() => value);
},
reason => {
return Promise.resolve(callback()).then(() => {
throw reason;
});
}
);
};
2
3
4
5
6
7
8
9
10
11
12
Promise.all
promise.all 是解决并发问题的,多个异步并发获取最终的结果(如果有一个失败则失败)
Promise.all = function(values) {
if (!Array.isArray(values)) {
const type = typeof values;
return new TypeError(`TypeError: ${type} ${values} is not iterable`);
}
return new Promise((resolve, reject) => {
let resultArr = [];
let orderIndex = 0;
const processResultByKey = (value, index) => {
resultArr[index] = value;
if (++orderIndex === values.length) {
resolve(resultArr);
}
};
for (let i = 0; i < values.length; i++) {
let value = values[i];
if (value && typeof value.then === "function") {
value.then(value => {
processResultByKey(value, i);
}, reject);
} else {
processResultByKey(value, i);
}
}
});
};
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
Promise.race
Promise.race 用来处理多个请求,采用最快的(谁先完成用谁的)。
Promise.race = function(promises) {
return new Promise((resolve, reject) => {
// 一起执行就是for循环
for (let i = 0; i < promises.length; i++) {
let val = promises[i];
if (val && typeof val.then === "function") {
val.then(resolve, reject);
} else {
// 普通值
resolve(val);
}
}
});
};
2
3
4
5
6
7
8
9
10
11
12
13
14