async 和 await 是 js 中的关键字,从字面意思上来看, async 是"异步", await 是"是等待异步", async 可以单独使用,用来声明一个异步函数,返回一个 Promise 对象,而 await 必须在 async 函数中使用
使用async
先来看看普通的函数
function test1() {
return 'hello async'
}
let a = test1()
console.log(b)
// 打印结果: 'hello async'
使用了async的函数
async function test2() {
return 'hello async'
}
let b = test2()
console.log(b)
// 打印结果: Promise {<fulfilled>: "hello async"}
由此可见,使用了 async 关键字的 test2 函数返回的是一个 Promise 对象,所以可以使用 then 的方式来异步执行获取结果,结果同 test1
test2().then(res => {
console.log(res)
})
// 打印结果:'hello async'
再来看看await
使用:
function testAsync() {
return Promise.resolve("hello await")
}
async function testAwait() {
const a = await testAsync()
console.log(a)
}
testAwait()
// 打印结果:'hello await'
使用 await 关键字只需注意一点即await 必须在 async 函数内使用
await的等待
首先承认 await 是一个运算符,一般来说使用 await 后面会等待一个 Promise 对象,但也有可能是一个普通的值,那么就有两种情况
- 当等待的是一个普通的值,那么 await 表达式运算的结果就是它等到的东西
- 当等待的是一个 Promise 对象,那 await 就忙了起来,它会阻塞后面的代码,等着 Promise resolve ,然后拿到 resolve 的值,作为 await 表达式的结果,这里的阻塞只会在当前 async 函数内阻塞,不会影响全局线程,这也是为什么 await 必须定义在 async 函数内
那么什么时候会用到 async / await
当同步的代码需要使用异步的代码获取的值
比如下面这样:
// 模拟异步的test函数 结果是10
function test() {
return new Promise(resolve => {
setTimeout(() => resolve(10), 1000);
});
}
// 函数fn
function fn() {
// 定义一个变量num
var num;
test().then(res => {
// 将test函数结果赋值给num
num = res
})
// 打印num
console.log(num) // undefined
}
// 调用函数fn
fn()
上面的代码可以看到打印 num 是 undefined,这是因为 test 函数是异步的,需要1秒后才能获取到值,并赋值给 num,但是我整体代码是同步的,可不会等你,所以 num 还未赋值,打印 num 出来当然是 undefined了
接下来使用 await,修改代码如下:
// 函数fn 使用 async / await
async function fn() {
// 定义一个变量num
var num;
await test().then(res => {
// 将test函数结果赋值给num
num = res
})
// 打印num
console.log(num) // 10
}
// 调用函数fn
fn()
成功了!这次打印 num 的结果是 10,分析下:当运行到 test 函数,await 发现它等到的是一个异步函数,于是它让"大家伙"把活先停停(阻塞),先等这个家伙干完我们再接着干(等到 test 异步函数的值),终于等到异步函数的值后,await 宣布大家继续干活,接下来就是将值赋给 num,然后打印 num,结果是 10,这就是上面例子的执行流程
async/await 的优势在于处理 then 链
单一的 Promise 链并不能体现出 async/await 的优势,但当需要处理由多个 Promise 组成的 then 链时, async/await 的优势就体现出来了
举个例子:
假如有三个步骤,每个步骤都是异步的,每个步骤的运算结果需要前一个步骤的运算结果,我们用 setTimeout 来模拟异步操作:
/**
* 传入参数 n,表示这个函数执行的时间(毫秒)
* 执行的结果是 n + 200,这个值将用于下一步骤
*/
function takeLongTime(n) {
return new Promise(resolve => {
setTimeout(() => resolve(n + 200), n);
});
}
function step1(n) {
console.log(`step1 with ${n}`);
return takeLongTime(n);
}
function step2(n) {
console.log(`step2 with ${n}`);
return takeLongTime(n);
}
function step3(n) {
console.log(`step3 with ${n}`);
return takeLongTime(n);
}
现在用 Promise 方式来实现这三个步骤的处理
function doIt() {
console.time("doIt");
const time1 = 300;
step1(time1)
.then(time2 => step2(time2))
.then(time3 => step3(time3))
.then(result => {
console.log(`result is ${result}`);
console.timeEnd("doIt");
});
}
doIt();
// c:\var\test>node --harmony_async_await .
// step1 with 300
// step2 with 500
// step3 with 700
// result is 900
// doIt: 1507.251ms
输出结果 result 是 step3() 的参数 700 + 200 = 900。doIt() 顺序执行了三个步骤,一共用了 300 + 500 + 700 = 1500 毫秒,和 console.time()/console.timeEnd() 计算的结果一致。
如果用 async/await 来实现呢,会是这样
async function doIt() {
console.time("doIt");
const time1 = 300;
const time2 = await step1(time1);
const time3 = await step2(time2);
const result = await step3(time3);
console.log(`result is ${result}`);
console.timeEnd("doIt");
}
doIt();
结果和之前的 Promise 实现是一样的,但是这个代码看起来是不是清晰得多,几乎跟同步代码一样
以上就是我对 async/await 的理解,有不对的地方欢迎指出,求知若饥,虚心若愚!