async与await
async
、await
是处理javascript中的异步问题的。
- async:声明函数时使用,用于表明该函数是一个异步函数。该函数返回值是一个
Promise
。既然是Promise,就可以用then
来回去值。 - await:只能在
async
标记的函数内使用。在函数调用时使用,表示阻塞直到这个函数返回。函数的返回结果如果是一个Promise,则相当于用then获取返回值而不是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
function a() {
console.log('a start');
return new Promise((resolve, reject) => {
console.log('a p start');
setTimeout(() => {
resolve('resolve a');
}, 5000)
})
}
function b() {
console.log('b start');
return new Promise((resolve, reject) => {
console.log('b p start');
setTimeout(() => {
return resolve('resolve b');
}, 6000)
})
}
async function t() {
console.log('t start');
let a1 = await a();
console.log('a end:', a1);
let b1 = await b();
console.log('b end:', b1);
console.log('t end');
return 'ok';
}
p = t()
p.then(res => {
console.log("res:",res)
// return Promise.resolve(res)
// return Promise.reject(res)
});
结果如下:
1
2
3
4
5
6
7
8
9
10
t start
a start
a p start
Promise {<pending>}
a end: resolve a
b start
b p start
b end: resolve b
t end
res: ok
通过观察结果发现,除了Promise {<pending>}
之外,其他的结果都是顺序执行的。这就是async
、await
的作用。那么这个Promise {<pending>}
是啥呢,其实是p.then
的返回结果,可以把里面的注释打开观察结果
1
2
3
4
5
6
7
8
9
10
11
// 打开 return Promise.resolve(res)
[[Prototype]]: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: "ok"
// 打开return Promise.reject(res)
[[Prototype]]: Promise
[[PromiseState]]: "rejected"
[[PromiseResult]]: "ok"
到这里基本上也就能明白async
、await
了,但是网上很多资料讲的更多,这里也多说一下
Generator函数
Generator
函数返回一个迭代器,通过内部的yield
和外部的next
来控制停止和运行。Generator
有如下特点:
1、Generator
函数再定义的时候function关键字和函数名之间有一个*;
2、Generator
函数的内部通过yield
来暂停
3、Generator
函数通过外部调用next
来继续执行,直到遇到下一个yield
暂停,或者return
结束。
1
2
3
4
5
6
7
8
9
10
function* fun() {
let a = yield 'aaa'
let b = yield 'bbb'
console.log(a + ' ' + b)
}
let d = fun()
console.log(d.next())
console.log(d.next())
console.log(d.next())
结果:
1
2
3
4
{value: 'aaa', done: false}
{value: 'bbb', done: false}
undefined undefined
{value: undefined, done: true}
注:可以看到这里的
next
方法是有返回值的,返回值是yield
后面的表达式的结果和运行状态组成的对象{value: yield后面的表达式的结果, done: 运行状态}
。
这里解释一下:
1、这里定义了一个Generator
函数,实例化let d = fun()
后并没有直接执行完成,而是执行到let a = yield 'aaa'
时因为有yield
暂停了,而且这个暂停是在给a
赋值前。
2、外面通过console.log(d.next())
第一次调用next
,输出结果{value: 'aaa', done: false}
,程序继续执行,给a
赋值,可是这时候没有值。因为yield
的值只能通过外面的next
传入。所有这时候a
是undefined
,然后运行let b = yield 'bbb'
3、同理遇到yield
暂停了,而且这个暂停是在给b
赋值前.
4、外面通过console.log(d.next())
,输出结果{value: 'bbb', done: false}
,同理b
是undefined
5、这时候没有yield
,继续运行console.log(a + ' ' + b)
,因为a
、b
都为undefined
所有输出undefined undefined
。
6、然后外面又调用console.log(d.next())
,这时候程序已经执行完成了,所有结果是{value: undefined, done: true}
到这里,可能比较迷茫,这个和async
、await
有啥关系吗?还真有。想一想,如果我们yield
后面跟的是一个Promise
,在next
的时候,使用then
处理来Promise
中的结果,在then
的回调函数中再进行下一个next
会咋样,是不是就是等第一个Promise
完成后,继续执行下一个Promise
,是不是就可以达到await效果。
Thunk函数
这里还要有一个能调用next
的函数。在JavaScript中Thunk函数是指将一个多参数函数替换成一个只接受回调函数作为参数的单参数函数。看着好像又没啥用。这个函数的作用就是包装一层:定义一个函数,这个函数的返回值是一个可以接受回调函数的函数。看下面的例子:
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
function p1() {
return new Promise((resolve, reject) => {
console.log('p1 start');
setTimeout(() => {
resolve('p1 callback');
}, 3000)
})
}
function p2() {
return new Promise((resolve, reject) => {
console.log('p2 start');
setTimeout(() => {
resolve('p2 callback');
}, 3000)
})
}
function* fun() {
console.log('fun start');
let v1 = yield p1();
console.log('v1:',v1);
let v2 = yield p2();
console.log('v2:',v2);
console.log(v1 + ' ' + v2);
console.log('fun end');
}
function tt(fn) {
return new Promise(function(resolve, reject) {
const f = fn();
function next(data) {
let result = f.next(data);
if (result.done) {
return resolve(result.value);
}
Promise.resolve(result.value).then(res => {
next(res); // Promise执行完成调用 next
})
}
next(undefined) // 第一次调用 next
})
}
tt(fun)
运行结果:
1
2
3
4
5
6
7
8
fun start
p1 start
Promise {<pending>}
v1: p1 callback
p2 start
v2: p2 callback
p1 callback p2 callback
fun end