Promise
# 前言
说到promise的出现,我们先得看看我们为什么会使用回调函数:考虑这样的应用场景,当我们的函数中具有异步操作时,我们如何监听这个异步操作呢?合理的情况是,我们可以同时监听异步操作的成功和失败,并且对异步操作的成功或者失败具有响应能力或者说控制权。但是当我们的异步操作很多,并且要求控制每一个异步操作的执行顺序,就难免会定义很多的回调函数,同时为了控制顺序,也会嵌套回调函数,当回调函数的层级很深的时候,就会产生所谓的回调地狱,而promise的出现正是为了解决这种困境。
# promise的简单介绍
正如其意,promise表示承诺的意思。promise可以类比生产者和消费者,生产者对消费者承诺一批商品,消费者通过某种方式和生产者进行连接,并使用之前规定好的方式消费生产者生产的商品。生产者只需要建立这种联系,将商品给到消费者,消费者消费的时候,商品或好或坏,两种结果下消费者会有不同的处理,但是商品的状态是不可逆的,可能是好东西或者是残次品。同时同一个商品不能同时存在两种状态,好东西和残次品应该是互斥的。
首先来看看生产者-promise构造函数
let promise = new Promise (function (resolve, reject) {
// 这就是生产者生产的商品
})
2
3
其中,传入promise构造函数
的函数被称作executor,这就是生产者和消费者建立联系的方式,通过向promise构造函数
中传入executor
。每一个executor
都有两个回调的函数,这是JS引擎内置的两个回调函数,不用我们自己定义。
- resolve(value) 如果任务成功就返回value
- reject(error) 如果任务执行失败 并且有error,就返回error对象
简单来说,executor
会执行某个任务,执行时间我们并不关心,任务执行成功我们就调用resolve
,任务执行失败我们就执行reject
。
每一个使用new promise
构造函数返回的对象都具有executor
属性,方便我们跟踪任务的进行状态:
- state: 最开始是pending,表示这是在等待状态;当任务执行成功后,调用resolve函数,state会变成fulfilled;任务执行失败后,调用reject函数,state会变成rejected。
- result: 最开始是undefined,调用resolve(value)的时候变成value, 或者在调用reject(erroe)的时候变成error。
# 1.正如前面提到的同一个商品只能有一个评价,state的状态也只能是一种
// 这里的宗旨是:只能是resolve或者reject,且只能存在一个。
let promise = new Promise(function(resolve, reject) {
resolve("done");
reject(new Error("…")); // 被忽略
setTimeout(() => resolve("…")); // 被忽略
});
2
3
4
5
6
7
如果executor
出现了什么错误,executor
会立即调用reject
函数,传入reject
的参数可以是任何值,但是推荐使用Error对象
,能更好的体现executor
发生了错误。
resolve
和reject
可以立即调用。一般来说,都是等待executor
运行的结果,然后自动调用resolve
和reject
这两个回调函数,但是我么也可以显示的调用resolve
和reject
let promise = new Promise(function(resolve, reject) {
resolve("done");
});
2
3
再来看消费者是如何消费的
消费者接受商品的方式主要是三种:
then
、catch
、finally
# 2.then
let promise = new Promise(function(resolve, reject) {
// 当 promise 被构造完成时,自动执行此函数
setTimeout(() => resolve("这个任务执行成功了!!"), 1000);
});
promise.then((result) => {
console.log(result); // 1秒后 这个任务执行成功了!!
})
2
3
4
5
6
7
8
消费者可以这样接收质量上乘的商品,但是遇到残次品呢?我们得这样:
let promise = new Promise(function(resolve, reject) {
// 当 promise 被构造完成时,自动执行此函数
setTimeout(() => reject(new Error("任务执行失败!!")), 1000);
});
promise.then((err) => {
console.log(err); // Error: 任务执行失败!!
})
2
3
4
5
6
7
8
then
方法有两个参数,第一个函数是接收executor
执行成功后的结果,第二个函数是接收executor
执行失败后的结果。
let promise = new Promise(function(resolve, reject) {
// 当 promise 被构造完成时,自动执行此函数
});
promise.then((result) => {
console.log(result);
},(err) => {
console.log(err);
})
2
3
4
5
6
7
8
9
# 3.catch
在上面说到当state
变为 的时候,可以使用then(null,rejection)``或then(undefined, rejection)
捕捉错误,但是promise
给我们提供了更加优雅的捕捉错误的做法,就是使用catch
,如下面的例子:
let promise = new Promise(function(resolve, reject) {
// 当 promise 被构造完成时,自动执行此函数
setTimeout(() => reject(new Error("任务执行失败!!")), 1000);
});
promise.catch(console.log) // Error: 任务执行失败!!
2
3
4
5
6
# 4.finally
不管executor执行的结果是成功还是失败,总有一些情况是都会处理的,比如清理不用多变量,这个时候就要用到finally:
let promise = new Promise(function(resolve, reject) {
// 当 promise 被构造完成时,自动执行此函数
setTimeout(() => reject(new Error("任务执行失败!!")), 1000);
});
promise.finally().then(null,(err) => {
console.log(err);
})
2
3
4
5
6
7
8
这里finally是将执行的结果进行传递,返回也是一个promise,可以使用then方法进行接收。
# 对promise的简单实现
一个完美的promise
应该符合promise A+规范,该规范详尽描述了promise的各种行为,我们先来实现一个简易版的promise
,简易版符合下面的要求:
- 当new MyPromise构造函数执行完毕,立即执行传入的函数executor
- 自定义的MyPromise类有三种状态,pending、fulfilled、rejected三种状态,初始状态都是pending。executor有两个回调函数参数,resolve和reject;当executor执行时,调用resolve,MyPromise类的state是fulfilled;调用reject,state变为rejected
- 存在一个then方法,也有两个回调参数,onFulfilled和onRejected;当onFulfilled不是函数时,直接返回onFulfilled的值;当onRejected不是函数时,直接返回原因。当onFulfilled和onRejected都是函数时,若是state时fulfilled时,直接调用onFulfilled;当state是rejected时,直接调用onRejected。
// 简易版promise
// 定义一个函数:判断传入的变量是否是函数
const isFunction = variable => typeof variable === 'function';
// 定义Promise的三种状态: pending、fulfilled、rejected
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
// 定义一个自己的Promise类
class MyPromise {
constructor(executor) {
if (!isFunction(executor)) {
return new Error("大兄弟,传入的不是函数!!")
}
this._state = PENDING; // 初始状态
this._value = null; //成功返回的值
this._reason = null; //失败返回的原因
try {
// 构造函数执行完成,立即执行executor函数(注意,需要使用bind绑定this的上下文)
executor(this._resolve.bind(this), this._reject.bind(this))
} catch (err) {
this._reject(err)
}
}
// resolve函数
_resolve (val) {
if (this._state !== PENDING) return;
this._state = FULFILLED;
this._value = val;
}
// reject函数
_reject (err) {
if (this._state !== PENDING) return;
this._state = REJECTED;
this._reason = err;
}
}
// 实现then方法
MyPromise.prototype.then = function(onFulfilled, onRejected) {
let realOnFulfilled = onFulfilled;
let realOnRejected = onRejected;
if (!isFunction(realOnFulfilled)) {
realOnFulfilled = function (value) {
return value;
}
}
if (!isFunction(realOnRejected)) {
realOnRejected = function (reason) {
if (reason instanceof Error) {
return reason;
} else {
throw new Error(reason)
}
}
}
// promise状态是fulfilled,会立即调用onFulfilled;
// promise状态是rejected, 会立即调用onRejected;
if (this._state === FULFILLED) {
realOnFulfilled(this._value)
}
if (this._state === REJECTED) {
realOnRejected(this._reason)
}
}
// 测试
let promise = new MyPromise((resolve, reject) => {
resolve(111)
}).then((res) => {
console.log(res); // 111
})
let promise = new MyPromise((resolve, reject) => {
reject("我错了")
}).then(null, (err) => {
console.log(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
可以看到,基本实现了最简单的promise
,在构造函数中resolve
和reject
时,我们可以在实例的then
方法中的第一个参数拿到promise
实例中resolve
的值;在第二个参数中拿到promise
实例中reject
的原因。
但是参考规范中then
方法的行为,我们这里的实现是远远达不到标准的,我们将then
方法的行为罗列下:
# 1.then方法的两个参数
- then方法的两个参数onFulfilled和onRejected,如果其值不是函数时,其值必须被忽略
对于onFulfilled的特性(onFulfilled是函数)
- promise为resolve(state是fulFilled)时调用onFulfilled,onFulfilled的第一个参数为resolve传入的值。
- 在promise状态改变前不可调用
- 调用次数不能超过一次
对于onRejected的特性(onRejected是函数)
- promise为reject(state是rejected)时调用onFulfilled,onRejected的第一个参数为reject传入的值。
- 在promise状态改变前不可调用
- 调用次数不能超过一次
# 2.then方法可以被同一个promise对象调用多次
- 当
promise
成功状态时,所有onFulfilled
需按照其注册顺序依次回调- 当
promise
失败状态时,所有onRejected
需按照其注册顺序依次回调
# 3.返回值
then方法返回一个promise对象
所以promise
支持链式调用
promise1.then(onFulfilled1, onRejected1).then(onFulfilled2, onRejected2);
这里链式调用规范也定义了各种情况下的行为规范,其实主要是值的传递和错误的捕获机制:
如果 onFulfilled
或者 onRejected
返回一个值 x
,则运行下面的 Promise
解决过程:[[Resolve]](promise2, x)
- 若
x
不为Promise
,则使x
直接作为新返回的Promise
对象的值, 即新的onFulfilled
或者onRejected
函数的参数.- 若
x
为Promise
,这时后一个回调函数,就会等待该Promise
对象(即x
)的状态发生变化,才会被调用,并且新的Promise
状态和x
的状态相同。
// 这里就是返回了一个值,所以promise1会将这个值传递给promise2的then方法
let promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
}, 1000)
})
promise2 = promise1.then(res => {
// 返回一个普通值
return '这里返回一个普通值'
})
promise2.then(res => {
console.log(res) //1秒后打印出:这里返回一个普通值
})
2
3
4
5
6
7
8
9
10
11
12
13
再来看这个例子:
// 这里是返回了一个promise,promise2会等待promise1和then方法中的回调函数执行完毕,并且拿到promise1的执行结果作为then方法回调函数的参数
let promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
}, 1000)
})
promise2 = promise1.then(res => {
// 返回一个Promise对象
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('这里返回一个Promise')
}, 2000)
})
})
promise2.then(res => {
console.log(res) //3秒后打印出:这里返回一个Promise
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
如果 onFulfilled
或者 onRejected
抛出异常e
,那个promise2的状态必须为rejected
,并且返回e
,例如:
let promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 1000)
})
promise2 = promise1.then(res => {
throw new Error('这里抛出一个异常e')
})
promise2.then(res => {
console.log(res)
}, err => {
console.log(err) //1秒后打印出:这里抛出一个异常e
})
2
3
4
5
6
7
8
9
10
11
12
13
如果onFulfilled
不是函数且 promise1
状态为成功(Fulfilled)
, promise2
必须变为成功(Fulfilled)
并返回 promise1
成功的值,例如:
let promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 1000)
})
promise2 = promise1.then('这里的onFulfilled本来是一个函数,但现在不是')
promise2.then(res => {
console.log(res) // 1秒后打印出:success
}, err => {
console.log(err)
})
2
3
4
5
6
7
8
9
10
11
如果 onRejected
不是函数且 promise1
状态为失败(Rejected)
,promise2
必须变为失败(Rejected)
并返回 promise1
失败的值,例如:
let promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('fail')
}, 1000)
})
promise2 = promise1.then(res => res, '这里的onRejected本来是一个函数,但现在不是')
promise2.then(res => {
console.log(res)
}, err => {
console.log(err) // 1秒后打印出:fail
})
2
3
4
5
6
7
8
9
10
11
我们需要完善下我们的then方法,首先使用数组存储我们的onFulfilled
和onRejected
两个回调函数,因为可能存在多个onFulfilled
和onRejected
:
// constructor构造函数中添加
constructor(executor) {
if (!isFunction(executor)) {
return new Error("大兄弟,传入的不是函数!!")
}
this._state = PENDING; // 初始状态
this._value = null; //成功返回的值
this._reason = null; //失败返回的原因,
this._onFulfilledCallbacks = [] // 存储链式调用时成功的函数
this._onRejectedCallbacks = [] // 存储链式调用失败的函数
try {
// 构造函数执行完成,立即执行executor函数(注意,需要使用bind绑定this的上下文)
executor(this._resolve.bind(this), this._reject.bind(this))
} catch (err) {
this._reject(err)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
重新定义then
方法
MyPromise.prototype.then = function(onFulfilled, onRejected) {
const {_state, _value} = this;
return new MyPromise((onFulfilledNext, onRejectedNext) => {
// 根据then方法的传值策略和捕获异常的规范,定义handleFulfilled和handleRejected函数
let handleFulfilled = _value => {
try {
if (!isFunction(onFulfilled)) {
onFulfilledNext(_value)
} else {
let res = onFulfilled(_value);
if (res instanceof MyPromise) {
// 如果当前回调函数返回的是promise对象,需要等待其执行完毕,再进行下一次回调
res.then(onFulfilledNext, onRejectedNext)
} else {
// 否则,将结果传递给下一次回调函数
onFulfilledNext(res)
}
}
} catch (err) {
onRejectedNext(err)
}
}
let handleRejected = error => {
try {
if (!isFunction(onRejected)) {
onRejectedNext(error)
} else {
let res = onRejected(error);
if (res instanceof MyPromise) {
// 如果当前回调函数返回的是promise对象,需要等待其执行完毕,再进行下一次回调
res.then(onFulfilledNext, onRejectedNext)
} else {
// 否则,将结果传递给下一次回调函数
onFulfilledNext(res)
}
}
} catch (err) {
onRejectedNext(err)
}
}
switch (_state) {
case FULFILLED:
onFulfilled(_value)
break;
case REJECTED:
onRejected(_value)
break;
case PENDING:
this._onFulfilledCallbacks.push(handleFulfilled)
this._onRejectedCallbacks.push(handleRejected)
break;
}
})
}
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
这里总算是对值的传递和错误的处理做了规范化,并且收集好了handleFulfilled
和handleRejected
,接着我们需要依次取出数组中的函数并执行:
// resolve函数
_resolve (val) {
if (this._state !== PENDING) return;
// 依次取出数组中的函数执行并清空数组
const run = () => {
this._state = FULFILLED;
this._value = val;
let cb;
while(this._onFulfilledCallbacks.shift()) {
cb(val)
}
}
// 为了支持同步的promise,这里异步调用
setTimeout(() => run(), 0)
}
// reject函数
_reject (err) {
if (this._state !== PENDING) return;
const run = () => {
this._state = REJECTED;
this._value = err;
let cb;
while(this._onRejectedCallbacks.shift()) {
cb(err)
}
}
setTimeout(() => run(), 0)
}
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
最后增加一些实例方法和静态方法:
// 添加catch方法
catch (onRejected) {
return this.then(undefined, onRejected)
}
// 添加resolve方法
static resolve (value) {
if (value instanceof MyPromise) return value;
return new MyPromise(resolve => resolve(value))
}
// 添加静态reject方法
static reject (value) {
return new MyPromise((resolve, reject) => reject(value))
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# promise的完整实现
// 实现简单的promise
// 定义一个函数:判断传入的变量是否是函数
const isFunction = variable => typeof variable === 'function';
// 定义Promise的三种状态: pending、fulfilled、rejected
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
// 定义一个自己的Promise类
class MyPromise {
constructor(executor) {
if (!isFunction(executor)) {
return new Error("大兄弟,传入的不是函数!!")
}
this._state = PENDING;
this._value = null; //成功返回的值
this._onFulfilledCallbacks = [] // 存储链式调用时成功的函数
this._onRejectedCallbacks = [] // 存储链式调用失败的函数
try {
executor(this._resolve.bind(this), this._reject.bind(this))
} catch (err) {
this._reject(err)
}
}
// executor的第一个参数 resolve方法
_resolve (val) {
if (this._state !== PENDING) return;
// 依次取出数组中的函数执行并清空数组
const run = () => {
this._state = FULFILLED;
this._value = val;
let cb;
while(cb = this._onFulfilledCallbacks.shift()) {
cb(val)
}
}
// 为了支持同步的promise,这里异步调用
setTimeout(() => run(), 0)
}
// executor的第二个参数 reject方法
_reject (err) {
if (this._state !== PENDING) return;
const run = () => {
this._state = REJECTED;
this._value = err;
let cb;
while(cb = this._onRejectedCallbacks.shift()) {
cb(err)
}
}
setTimeout(() => run(), 0)
}
// 实现then方法
then (onFulfilled, onRejected) {
const {_state, _value} = this;
return new MyPromise((onFulfilledNext, onRejectedNext) => {
// 根据then方法的传值策略和捕获异常的规范,定义handleFulfilled和handleRejected函数
let handleFulfilled = _value => {
try {
if (!isFunction(onFulfilled)) {
onFulfilledNext(_value)
} else {
let res = onFulfilled(_value);
if (res instanceof MyPromise) {
// 如果当前回调函数返回的是promise对象,需要等待其执行完毕,再进行下一次回调
res.then(onFulfilledNext, onRejectedNext)
} else {
// 否则,将结果传递给下一次回调函数
onFulfilledNext(res)
}
}
} catch (err) {
onRejectedNext(err)
}
}
let handleRejected = error => {
try {
if (!isFunction(onRejected)) {
onRejectedNext(error)
} else {
let res = onRejected(error);
if (res instanceof MyPromise) {
// 如果当前回调函数返回的是promise对象,需要等待其执行完毕,再进行下一次回调
res.then(onFulfilledNext, onRejectedNext)
} else {
// 否则,将结果传递给下一次回调函数
onFulfilledNext(res)
}
}
} catch (err) {
onRejectedNext(err)
}
}
switch (_state) {
case FULFILLED:
onFulfilled(_value)
break;
case REJECTED:
onRejected(_value)
break;
case PENDING:
this._onFulfilledCallbacks.push(handleFulfilled)
this._onRejectedCallbacks.push(handleRejected)
break;
}
})
}
// 添加catch方法
catch (onRejected) {
return this.then(undefined, onRejected)
}
// 添加resolve方法
static resolve (value) {
if (value instanceof MyPromise) return value;
return new MyPromise(resolve => resolve(value))
}
// 添加静态reject方法
static reject (value) {
return new MyPromise((resolve, reject) => reject(value))
}
}
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
# promisify
promisify 是指将具有异步操作的行为promise化,因为promise确实很适合干这个
如下面这个例子:
function loadScript(src, callback) {
let script = document.createElement('script');
script.src = src;
script.onload = () => callback(null, script);
script.onerror = () => callback(new Error(`Script load error for ${src}`));
document.head.append(script);
}
// 用法:
// loadScript('path/script.js', (err, script) => {...})
2
3
4
5
6
7
8
9
10
11
12
为了可读性,我们可以将loadScript promise化
:
let loadPromise = function (src) {
return new Promise((resolve, reject) => {
loadScript(src, (err,script) => {
if (err) reject(err);
else resolve(script)
})
})
}
2
3
4
5
6
7
8