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
