Promise
Promise 构造函数
const executor = (resolve, reject) {
if (条件为真) {
resolve()
} else {
reject()
}
}
const promiseInstance = new Promise(executor)
2
3
4
5
6
7
8
通过new Promise(executor)
创建 Promise 实例时,需要给Promise
构造函数传递一函数作为参数,该函数有两个参数,分别是resolve
和reject
,调用resolve
或reject
将 Promise 实例的状态改变为resolved
或rejected
状态。
resolve 的参数是 Promise 实例
在创建promiseA
实例的构造函数的参数fn
执行过程中,若调用resolve
时传入的参数是另一个 Promise 实例promiseB
,则此时promiseB
的状态将传递给promiseA
,也就是说,promiseB
的状态决定了promiseA
的状态。
如果promiseB
的状态是pending
,那么promiseA
就会等待promiseB
的状态改变。
如果promiseB
的状态是resolved
/rejected
,那么promiseA
的状态也会变成resolved
/rejected
。
var promiseB = new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('fail')), 3000)
})
var promiseA = new Promise(function (resolve, reject) {
setTimeout(() => resolve(promiseB), 1000)
})
promiseA.then(result => {
console.log(result)
}).catch(error => {
console.log(error)
})
// 执行结果:
// Error: fail
2
3
4
5
6
7
8
9
10
11
12
13
14
15
警告
注意,上述只是针对调用resolve
时传入的参数是 Promise 实例来说的,调用reject
时传入的参数是 Promise 实例则无类似效果,会直接将promiseA
的状态改变为Rejected
。
var promiseB = new Promise(function (resolve, reject) {
setTimeout(() => resolve('promiseB resolved!'), 3000)
})
var promiseA = new Promise(function (resolve, reject) {
setTimeout(() => reject(promiseB), 1000)
})
promiseA.then(result => {
console.log('then')
}).catch(error => {
console.log('error 是否是 promise 实例:', error instanceof Promise)
})
// 执行结果:
// error 是否是 promise 实例: true
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Promise.prototype.then/catch 方法
then/catch 方法返回新的 Promise 实例
Promise 实例的then/catch
方法返回一个新的 Promise 实例,因此可以采用链式写法,即then/catch
方法后面再调用另一个then/catch
方法。以then
方法为例:
function getJSON(data) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(data)
}, 100)
})
}
getJSON("/posts.json").then(
json => {
console.log('第一个 resolved 状态的回调函数,其参数为: ' + json)
return 'hello';
}
).then(
param => {
console.log('第二个 resolved 状态的回调函数,其参数为: ' + param)
}
)
// 执行结果:
// "第一个 resolved 状态的回调函数,其参数为: /posts.json"
// "第二个 resolved 状态的回调函数,其参数为: hello"
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
上面的代码链式调用了两次then
方法,并分别指定了两个resolved
状态的回调函数,第一个then
方法的resolved
回调函数完成之后,会将返回结果作为参数,传入第二个then
方法的resolved
回调函数。
resolved/rejected 回调函数返回另一 Promise 实例
如果第一个then
方法添加的resolved/rejected
回调函数执行后return
的是另一个 Promise 实例,这时第二个then
添加的回调函数就会等待这个返回的 Promise 实例的状态发生变化后才会被调用,如果返回的 Promise 实例的状态变为rejected
,则调用rejected
回调函数,变为resolved
则调用resolved
回调函数。
function getJSON(data) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(data)
}, 100)
})
}
function getNone(data) {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject(data)
}, 100)
})
}
getJSON("/posts.json").then(
data => {
console.log('第一个 then 方法添加的 resolved 回调函数: ' + data)
return getNone('reject啦~');
}
).then(
data => {
console.log('第二个 then 方法添加的 resolved 回调函数: ' + data)
},
err => {
console.log('err:' + err)
}
)
// 执行结果:
// "第一个 then 方法添加的 resolved 回调函数: /posts.json"
// "err:reject啦~"
getNone("none~").then(
json => {
console.log('第一个 then 方法添加的 resolved 回调函数: ' + data)
return getNone('reject啦~');
},
err => {
console.log('第一个 then 方法添加的 rejected 回调函数:' + err)
return getNone('none~~~222')
}
).then(
param => {
console.log('第二个回调函数的参数: ' + param)
},
err => {
console.log('第二个 then 方法添加的 rejected 回调函数:' + err)
}
)
// 执行结果:
// "第一个 then 方法添加的 rejected 回调函数:none~"
// "第二个 then 方法添加的 rejected 回调函数:none~~~222"
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
Promise.prototype.catch
Promise.prototype.catch(rejected)
方法是Promise.prototype.then(null, rejected)
的别名。
如果异步操作执行抛错,即fn
函数执行时抛错,Promise 实例的状态就会变为 Rejected。
// 写法一
var promise = new Promise(function(resolve, reject) {
throw new Error('test');
}).catch(function(error) {
console.log(error);
});
// 写法二
var promise = new Promise(function(resolve, reject) {
try {
throw new Error('test');
} catch(e) {
reject(e);
}
}).catch(function(error) {
console.log(error);
});
// 写法三
var promise = new Promise(function(resolve, reject) {
reject(new Error('test'));
}).catch(function(error) {
console.log(error);
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
比较上面几种写法,可以发现reject
函数的作用,等同于抛出错误。
如果 Promise 状态已经变成resolved
,再抛出错误是无效的。
var promise = new Promise(function(resolve, reject) {
resolve('ok');
console.log('resolve 之后还会执行')
throw new Error('test');
console.log('但是抛错及以后代码都不会执行 ')
}).then(function(value) { console.log(value) })
.catch(function(error) { console.log(error) });
2
3
4
5
6
7
一般来说,不要在then
方法里面定义rejected
回调函数(即then
的第二个参数),总是使用catch
方法。
// bad
promise.then(function(data) {
// success
}, function(err) {
// error
});
// good
promise.then(function(data) {
// success
}).catch(function(err) {
// error
});
2
3
4
5
6
7
8
9
10
11
12
13
14
跟传统的try/catch
代码块不同的是,如果没有使用catch
方法指定错误处理的rejected
回调函数,fn
抛出的错误不会传递到外层代码,即不会有任何反应。
var someAsyncThing = function() {
return new Promise(function fn(resolve, reject) {
// 下面一行会报错,因为x没有声明
resolve(x + 2);
});
};
someAsyncThing().then(function() {
console.log('everything is great');
});
setTimeout(() => { console.log(123) }, 2000);
// Uncaught (in promise) ReferenceError: x is not defined
// 123
2
3
4
5
6
7
8
9
10
11
12
13
14
上面代码中,someAsyncThing
函数里的fn
函数执行时会报错,但是由于没有指定catch
方法,这个错误不会被捕获,也不会传递到外层代码。正常情况下,运行后不会有任何输出,但是浏览器此时会打印出错误ReferenceError: x is not defined
,但是不会退出进程、终止脚本执行,2 秒之后还是会输出123
。这就是说,Promise 内部的错误不会影响到 Promise 外部的代码,通俗的说法就是“Promise 会吃掉错误”。
这个脚本放在服务器执行,退出码就是0
(即表示执行成功)。不过,Node 有一个unhandledRejection
事件,专门监听未捕获的reject
错误,上面的脚本会触发这个事件的监听函数,可以在监听函数里面抛出错误。
process.on('unhandledRejection', function (err, p) {
throw err;
});
2
3
上面代码中,unhandledRejection
事件的监听函数有两个参数,第一个是错误对象,第二个是报错的 Promise 实例,它可以用来了解发生错误的环境信息。
then/catch 返回的 Promise 实例的状态
then/catch
里的resolved/rejected
回调函数执行如果不抛错,则返回的 Promise 实例的状态变为resolved
,回调函数的返回值(没有return
则为undefined
)作为参数;抛错,状态变为rejected
。
var promise = new Promise(function(resolve, reject) {
resolve('ok');
}).then(function(data) {
console.log(data)
}).then(function(data) {
console.log(data)
});
// 执行结果
// "ok"
// undefined
var promise = new Promise(function(resolve, reject) {
reject('err');
}).then(
value => console.log(value),
err => console.log(err)
).then(
value => console.log(value)
)
// 执行结果
// "err"
// undefined
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Promise.all()
Promise.race()
Promise.resolve()
Promise 实现
Promises/A+ 规范
Promises/A+ 规范文档
代码实现参考
重难点问题
then 方法链式调用后为什么要返回新的 promise 实例?
如我们理解,为保证then
函数链式调用,then
需要返回promise
实例。但为什么返回新的promise
,而不直接返回this
当前对象呢?看下面示例代码:
const promise2 = promise1.then(function (value) {
return Promise.reject(3);
})
2
3
假如then
函数执行返回this
调用对象本身,那么promise2 === promise1
,promise2
状态也应该等于promise1
同为resolved
。而onResolved
回调中返回状态为rejected
对象。考虑到Promise
状态一旦resolved
或rejected
就不能再迁移,所以这里promise2
也没办法转为回调函数返回的rejected
状态,产生矛盾。
通过 then 注册的回调函数为什么要异步执行?
var a = 1;
promise1.then(function (value) {
a = 2;
})
console.log(a)
2
3
4
5
6
7
promise1
内部执行同步或异步操作未知。
假如未规定then
注册回调为异步执行,则这里打印a
可能存在两种值。promise1
内部同步执行操作时a === 2
,相反执行异步操作时a === 1
。为屏蔽依赖外部的不确定性,规范指定onFulfilled
和onRejected
方法异步执行。
简单实现源码
// 用于异步执行通过 promise.then() 方法添加的回调函数
const asyncFn = (function() {
if (
typeof process === 'object' &&
process !== null &&
typeof process.nextTick === 'function'
) {
// microtask
return process.nextTick;
} else if (typeof setImmediate === 'function') {
// macrotask
return setImmediate;
}
// macrotask
return setTimeout;
})();
function Handler(onResolved, onRejected, promise) {
this.onResolved = typeof onResolved === 'function' ? onResolved : null;
this.onRejected = typeof onRejected === 'function' ? onRejected : null;
this.promise = promise;
}
function Promise(fn) {
// 省略非 new 实例化方式处理
// 省略 fn 非函数异常处理
// promise 状态变量
// 0 - pending
// 1 - resolved
// 2 - rejected
this._state = 0;
// promise 执行结果
this._value = null;
// then(..) 注册回调处理数组
this._deferreds = [];
// 立即执行 fn 函数
try {
fn(
value => {
resolve(this, value);
},
reason => {
reject(this, reason);
}
);
} catch (err) {
reject(this, err);
}
}
Promise.prototype.then = function(onResolved, onRejected) {
const res = new Promise(function() {});
// 使用 onResolved,onRejected 实例化处理对象 Handler
const deferred = new Handler(onResolved, onRejected, res);
if (this._state === 0) {
// 当前状态为 pendding,存储延迟处理对象
this._deferreds.push(deferred);
} else {
// 当前 promise 状态不为 pending,调用 handleResolved 执行 onResolved 或 onRejected 回调
handleResolved(this, deferred);
}
// 返回新 promise 对象,维持链式调用
return res;
};
function resolve(promise, value) {
// 非 pending 状态不可变
if (promise._state !== 0) {
return;
}
// Promise A+ 规范 2.3.1
// value 和 promise 指向同一对象
if (value === promise) {
return reject(
promise,
new TypeError('A promise cannot be resolved with itself.')
);
}
// Promise A+ 规范 2.3.2
// value 和 promise 为 相同实现的 Promise,则使 promise 接受 value 的状态
if (value && value instanceof Promise && value.then === promise.then) {
const oldDeferreds = promise._deferreds;
if (value._state === 0) {
// Promise A+ 规范 2.3.2.1
// value 为 pending 状态
// 将 promise._deferreds 传递 value._deferreds
// 偷个懒,使用 ES6 展开运算符
value._deferreds.push(...oldDeferreds);
} else if (oldDeferreds.length) {
// Promise A+ 规范 2.3.2.2、2.3.2.3
// value 为 非 pending 状态
// 使用 value 作为当前 promise,执行 then 注册回调处理
oldDeferreds.forEach(deferred => {
handleResolved(value, deferred);
});
// 清空 then 注册回调处理数组
value._deferreds = [];
}
return;
}
// Promise A+ 规范 2.3.3
// value 是对象或函数
if (value && (typeof value === 'object' || typeof value === 'function')) {
let then;
try {
// Promise A+ 规范 2.3.3.1
then = value.then;
} catch (err) {
// Promise A+ 规范 2.3.3.2
reject(err);
}
// Promise A+ 规范 2.3.3.3
// 如果 then 是函数,将 value 作为函数的作用域 this 调用之
if (typeof then === 'function') {
try {
// 执行 then 函数
then.call(
value,
function(value) {
resolve(promise, value);
},
function(reason) {
reject(promise, reason);
}
);
} catch (err) {
reject(promise, err);
}
return;
}
}
// Promise A+ 规范 2.3.3.4、2.3.4
// 改变 promise 内部状态为 `resolved`
promise._state = 1;
promise._value = value;
// promise 存在 then 注册回调函数
promise._deferreds.forEach(function(deferred) {
handleResolved(promise, deferred);
});
promise._deferreds = [];
}
function reject(promise, reason) {
// 非 pending 状态不可变
if (promise._state !== 0) {
return;
}
// 改变 promise 内部状态为 `rejected`
promise._state = 2;
promise._value = reason;
promise._deferreds.forEach(deferred => {
handleResolved(promise, deferred);
});
promise._deferreds = [];
}
function handleResolved(promise, deferred) {
// 异步执行注册回调
asyncFn(function() {
const cb =
promise._state === 1 ? deferred.onResolved : deferred.onRejected;
// 传递注册回调函数为空情况
if (cb === null) {
if (promise._state === 1) {
resolve(deferred.promise, promise._value);
} else {
reject(deferred.promise, promise._value);
}
return;
}
// 执行注册回调操作
let res;
try {
res = cb(promise._value);
} catch (err) {
reject(deferred.promise, err);
}
// 处理链式 then(..) 注册处理函数调用
resolve(deferred.promise, res);
});
}
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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201