Promise介绍与自定义封装、async/await介绍
# 异步编程
ES6新增了正式的 Promise 引用类型,支持优雅地定义和组织异步逻辑,是JS中进行异步编程的新解决方案(旧方案是使用回调地狱)。接下来几个版本增加了使用 async 和 await 关键字定义异步函数的机制。
回调函数并不是函数自调用,而是将函数作为参数。一般情况下,把函数作为参数的目的就是为了获取函数内部的异步操作结果。异步是因为js是单线程的,对一些耗时的异步操作放在后面执行可提高性能。
异步操作:
- fs 文件操作(readFile/writeFile/readdir)
- 数据库操作
- setTimeout
- ajax
# Promise介绍与基本使用
Promise 是一个构造函数,用来封装一个异步操作并可以获取成功/失败的结果值。Promise支持链式调用,可以解决回调地狱问题。
使用:
const p = new Promise((resolve, reject) => {
// 异步操作
if(...) {
resolve();
}else{
reject();
}
});
p.then(() => {
//成功后执行
}, () => {
//失败后执行
})
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# Promise的状态
Promise实例对象中有一个表示状态的属性PromiseState
。有三种状态:
- pending 待定
- resolved/fulfilled 成功
- rejected 失败
只有 pending->resolved 和 pending->rejected 两种状态变化的可能,且一个Promise对象只能改变一次,无论变为成功还是失败,都会有一个结果数据,成功的结果数据一般称为value,失败的一般称为reason。
# Promise对象的值
PromiseResult
保存的是异步任务成功或失败的结果。
# Promise API
- Promise构造函数:Promise(executor){}
- executor为执行器函数,即
(resolve,reject)=>{}
- executor会在Promise内部立即同步调用,不会进入任务队列,异步操作在执行器中执行
- executor为执行器函数,即
- Promise.prototype.then(onResolved,onRejected){}
- then方法里面的代码是异步执行的
- onResolved函数:成功时的回调函数
- onRejected函数:失败时的回调函数
- Promise.prototype.catch(onRejected){}
- 只能指定失败的回调函数
- Promise.resolve(value=>{})
- 如果value为非Promise类型的对象,则返回的结果为成功promise对象
- 如果value为Promise 对象,则参数的结果决定了resolve的结果
- Promise.reject(reason=>{})
- 将参数快速转换成一个失败的promise对象
- Promise.all(promises=>{})
- promises是包含n个promise的数组
- 返回一个新的promise(如果成功,则是所有promise对象结果组成的数组),只有所有的promise都成功才成功,只要有一个失败了就直接失败
- Promise.race(promises=>{})
- 返回一个新的promise,第一个完成的promise的结果状态就是最终的结果状态
注:Promise.prototype.fun()表示该函数是Promise实例对象的方法,而Promise.fun()表示该函数是Promise函数对象的方法。
# Promise关键问题
- 如何改变promise的状态?
- 调用
resolve
或reject
函数 - 使用
throw
抛出错误
- 调用
- 一个promise指定(用then或catch方法指定)多个成功or失败回调函数,都会调用吗,即能否执行多个回调?
- 当promise改变为对应状态时都会调用
- 改变promise状态(resolve or reject)和指定回调函数(then or catch)谁先谁后?
- 都有可能,正常情况下是先指定回调函数再改变状态
- 如何先改状态再指定回调?
- 当执行器中的任务是同步任务时,直接调用resolve()/reject()
- 延迟更长时间才调用then()
- 什么时候才能得到数据,即回调函数什么时候执行?
- 如果先指定的回调,那当状态发生改变时,回调函数就会调用,得到数据
- 如果先改变的状态,那当指定回调时,回调函数就会调用,得到数据
- Promise.prototype.then()返回的新promise的结果状态由什么决定?
- 如果抛出异常,新promise变为rejected, reason为抛出的异常
- 如果返回的是非promise的任意值,新promise变为resolved, value为返回的值
- 如果返回的是另一个新promise,此promise的结果就会成为新promise的结果
- promise如何串连多个操作任务?
- 通过then的链式调用串连多个操作任务,因为then返回的是一个promise
- 如果有多个then链式调用,只有在上一个then执行后确定了状态才会执行下一个then中回调函数,即上一个then的回调函数执行后,下一个then的回调函数才会进入微队列
- promise异常穿透?
- 当使用promise的then链式调用时,可以在最后指定失败的回调,可以用then或catch,用catch只要传一个参数
- 前面任何操作出了异常,都会传到最后失败的回调中处理
- 中断promise链?
- 在then的链式调用中,如果要中断链式调用,就必须返回一个pending状态的promise
return new Promise(() => {})
- 此时状态没有改变,所以后续promise链不会再继续执行
# Promise自定义封装
# 封装promise构造函数
//声明构造函数
function Promise(executor) {
//添加属性
this.PromiseState = 'pending';
this.PromiseResult = null;
//保存多个回调
this.callbacks = [];
//保存实例对象的this值
const self = this;
//resolve 函数
function resolve(data) {
//保证状态只能更改一次
if(self.PromiseState !== 'pending') return;
//这里的this指向window
//1. 修改对象的状态 (PromiseState)
self.PromiseState = 'fulfilled';
//2. 修改对象结果值(PromiseResult)
self.PromiseResult = data;
//调用成功的回调函数
setTimeout(() => {
self.callbacks.forEach(item => {
item.onResolved(data);
});
})
}
//reject 函数
function reject(data) {
//保证状态只能更改一次
if(self.PromiseState !== 'pending') return;
//1. 修改对象的状态 (PromiseState)
self.PromiseState = 'rejected';
//2. 修改对象结果值(PromiseResult)
self.PromiseResult = data;
//调用失败的回调函数
setTimeout(() => {
self.callbacks.forEach(item => {
item.onRejected(data);
});
})
}
try {
//同步调用【执行器函数】
executor(resolve, reject);
}catch(e) {
//修改 promise 对象状态为失败
reject(e);
}
}
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
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
# 封装then和catch方法
//添加then方法
Promise.prototype.then = function(onResolved, onRejected) {
const self = this;
//判读失败的回调函数有没有传,因为默认用户可以不传这个参数
if(typeof onRejected !== 'function') {
//添加默认值,是异常穿透的原理
onRejected = reason => {
throw reason;
}
}
//如果成功的回调函数没有传
if(typeof onResolved !== 'function') {
onResolved = value => value;
}
//返回promise
return new Promise((resolve, reject) => {
//封装函数
function callback(type) {
//获取回调函数的执行结果(链式调用需要)
let result = type(self.PromiseResult);
//判断result类型
if(result instanceof Promise) {
result.then(v => {
resolve(v);
}, r => {
reject(r);
})
}else{
//如果返回的不是promise类型,就将状态改为成功
resolve(result);
}
}
//如果promise中的是同步操作
if(this.PromiseState === 'fulfilled') {
setTimeout(() => {
callback(onResolved);
})
}
if(this.PromiseState === 'rejected') {
setTimeout(() => {
callback(onRejected);
})
}
//如果promise中的是异步操作
//判断pending状态
/* 为什么要判断 pending 状态?
如果promise中是异步操作,那么异步操作会进入等待队列,接着执行下面的then方法
promise的状态改变是在异步操作完成后执行的,也就是说执行到then方法时,状态还没发生改变,还是pending状态
这时候状态没有改变,但后面会变成什么状态是不确定的,所以要将回调函数保存起来,等状态发生改变时再调用
*/
if(this.PromiseState === 'pending') {
this.callbacks.push({
onResolved: () => {callback(onResolved);},
onRejected: () => {callback(onRejected);}
});
}
})
}
//添加catch方法
Promise.prototype.catch = function(onRejected) {
return this.then(undefined, 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
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
# promise函数对象方法封装
//添加resolve方法
Promise.resolve = function(value) {
//返回promise对象
return new Promise((resolve, reject) => {
if(value instanceof Promise) {
value.then(v => {
resolve(v);
}, r => {
reject(r);
})
}else{
resolve(value);
}
});
}
//添加reject方法
Promise.reject = function(reason) {
return new Promise((resolve, reject) => {
reject(reason);
});
}
//添加all方法
Promise.all = function(promises) {
//返回结果为promise对象
return new Promise((resolve, reject) => {
let count = 0;
let arr = [];
for(let i=0; i<promises.length; i++) {
promises[i].then(v => {
count++;
arr[i] = v;
if(count === promises.length){
resolve(arr);
}
}, r => {
reject(r);
});
}
})
}
//添加race方法
Promise.race = function(promises) {
return new Promise((resolve, reject) => {
for(let i=0; i<promises.length; i++) {
promises[i].then(v => {
//这里直接调用就可以,因为状态只可以改变一次,所以第一次改变的那个就是最后的结果
resolve(v);
}, r => {
reject(r);
});
}
})
}
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
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
# class版的promise封装
class Promise{
constructor(executor) {...}
//属于实例对象的方法
then(onResolved, onRejected) {...}
catch(onRejected) {...}
//属于类的静态方法
static resolve(value) {...}
static reject(reason) {...}
static all(promises) {...}
static race(promises) {...}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# async 与 await
# async函数
- 函数返回值为promise对象,成功或失败规则与Promise.resolve一致
- promise对象的结果由async函数执行的返回值决定
async function main() {...}
1
# await表达式
- await右侧的表达式一般为promise对象,但也可以为其他值
- 如果表达式是promise对象, await返回的是promise成功的值
- 如果表达式是其它值,直接将此值作为await的返回值
注:
- await必须写在async函数中,但async函数中可以没有await
- 如果await的promise失败了,就会抛出异常,需要通过try...catch捕获处理
async function main(){
let p = new Promise((resolve,reject) => {
resolve("ok");
});
let res = await p;
console.log(res);// ok
let res1 = await 20;
console.log(res1);// 20
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 小栗子
function sendAjax(url) {
return new Promise((resolve, reject) => {
//发送ajax请求
...
})
}
btn.addEventListener('click', async function() {
let data = await sendAjax('https://...');
})
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10