async

Async +Await

async 是“异步”的简写,async 用于申明一个 function 是异步的,而 await 用于等待一个异步方法执行完成,await 只能出现在 async 函数中

async 的作用

async 函数负责返回一个 Promise 对象
如果在async函数中 return 一个直接量,async 会把这个直接量通过Promise.resolve() 封装成 Promise 对象;
如果 async 函数没有返回值,它会返回 Promise.resolve(undefined)

1
2
async function asyncTest0(){}
console.log(asynctest()) // 输出一个Promise对象 Promise.resolve(undefined)

await 在等待什么

一般我们都用await去等一个async函数完成,不过按语法说明,await 等待的是一个表达式,这个表达式的计算结果是 Promise 对象或者其它值,所以,await后面实际可以接收普通函数调用或者直接量

如果await等到的不是一个promise对象,那跟着的表达式的运算结果就是它等到的东西;
如果是一个promise对象,await会阻塞后面的代码,等promise对象resolve,得到resolve的值作为await表达式的运算结果
虽然await阻塞了,但await在async中,async不会阻塞,它内部所有的阻塞都被封装在一个promise对象中异步执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let a=0
async function asyncTest1(){
// await 只对Promise生效
await new Promise((resolve) => {
setTimeout(_=>{
a++
resolve()
},0)})
console.log('0:a='+a)
}
asyncTest1()
console.log('1:a='+a)

//运行结果:
// 1:a=0
// 0:a=1

上面的🌰中, async函数asyncTest1被封装成在一个promise对象异步执行, 所以先输出同步执行的1 :a=0, 此时a++还没执行估a为0. 执行asyncTest1函数, await会阻塞后面的代码, 所以输出0: a=1时, a++已经执行完了.

循环

🌰:

模拟异步:

1
2
3
4
5
6
7
8
9
// 模拟异步
function fetch(d){
return new Promise((resolve)=>{
console.log('start:',d);
setTimeout(()=>{
resolve(d)
},Math.random()*1000);
})
}

使用forEach并行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
async function test0() {
let args = [1,2,3];
// 可能得到错误结果
//原因是这时三个 db.post 操作将是并发执行,也就是同时执行,而不是继发执行。
args.forEach(async function (arg) {
await fetch(arg).then(_=>{console.log('end:'+arg)});
});
// 因为此时forEach中重新启动async 函数,是异步的
// 此时并没有等到所有的http.post运行结束
}
test0()
/*输出
start: 1
start: 2
start: 3
Promise {<pending>}
end: 3
end: 2
end: 1
*/
// end的输出顺序不固定

使用for循环串行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
async function test1() {
let args = [1,2,3];
for (let arg of args) {
// 注意: 此处的函数会顺序继发执行, 即函数执行是串行
await fetch(arg).then(_=>{console.log('end:'+arg)});
}
// 此处在所有 http.post 执行后执行
}
test1()
/* 输出
start: 1
Promise {<pending>}
end:1
start: 2
end:2
start: 3
end:3
*/

多个请求并发执行:

可用Promise.all

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
var num = 0
async function test2() {
let args = [1,2,3];
let promises = args.map((arg) =>{
num++
return fetch(arg).then(_=>{console.log('end:'+arg)})
});
// await等到Promise.all多个请求并发执行结束
let results = await Promise.all(promises);
// 此处在所有 Promise.all 执行后执行
console.log('num='+num)
}

test2()
/* 输出
start: 1
start: 2
start: 3
Promise {<pending>}
end: 3
end: 2
end: 1
num= 3
*/
// end的输出顺序不固定

或者

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
var num = 0
async function test3() {
let args = [1,2,3];
let promises = args.map((arg) =>{
return fetch(arg).then(_=>{
num++ ;
console.log('end:'+arg)
})
});
let results = [];
for (let promise of promises) {
results.push(await promise);
}
console.log(num);
}
test3()
/*
start: 1
start: 2
start: 3
Promise {<pending>}
end:2
end:1
end:3
3
*/
// end的输出顺序不固定

异常处理

异常

async 函数返回的是一个promise对象, 估可以直接使用.catch捕获异常

1
2
3
4
5
6
7
8
9
10
11
12
async function f() {
await new Promise(function (resolve, reject) {
throw new Error('出错了');
});
console.log('---')
await Promise.resolve('1')
}
f()
.then(v => console.log('then:'+v))
.catch(e => console.log('catch:'+e))
// 输出:
// catch:Error: 出错了

或者:

await会等待执行结束, 所以代码其实相当于一个同步的过程, 可以直接采用try…catch捕获

1
2
3
4
5
6
7
8
9
10
11
12
13
14
async function f() {
try {
await new Promise(function (resolve, reject) {
throw new Error('出错了');
});
console.log('---')
await Promise.resolve('1')
} catch(e) {
console.log(e)
}
}
f()
// 输出:
// catch:Error: 出错了

注意: 第二个 await没有执行(—没有打印), 也就证明, async遇到异常就会中断链