async 是什么?
async function 声明将定义一个返回 AsyncFunction 对象的异步函数。
一些异步函数变体:
- 异步函数声明: ` async function foo() {}`
- 异步函数表达式:
const foo = async function () {};
- 异步函数定义:
let obj = { async foo() {} }
- 异步箭头函数:
const foo = async () => {};
我们来看async到底返回了什么:
async function testAsync() {
return "hello async";
}
const result = testAsync();
console.log(result); // Promise { 'hello async' }
async 函数会返回一个 Promise 对象,如果在函数中return一个直接量,async 会把这个直接量通过 Promise.resolve()
封装成 Promise 对象:
由于返回的是一个promise对象,用原来的方式:then()链来处理这个promise对象:
testAsync().then(v => {
console.log(v); // 输出 hello async
});
如果async 函数没有返回值,返回的对象则是: Promise.resolve(undefined)
联想一下Promise 的特点——无等待,所以在没有 await 的情况下执行 async 函数,它会立即执行,返回一个 Promise 对象,并且,绝不会阻塞后面的语句。这和普通返回 Promise 对象的函数并无二致。
很明显,关键在于await
await
语法: ` [return_value] = await expression;`
表达式 : 一个 Promise 对象或者任何要等待的值。 返回值 : 返回 Promise 对象的处理结果。如果等待的不是 Promise 对象,则返回该值本身。
await 表达式会暂停当前 async function 的执行,等待 Promise 处理完成。
若 Promise 正常处理(fulfilled),其回调的resolve函数参数作为 await 表达式的值,继续执行 async function。
若 Promise 处理异常(rejected),await 表达式会把 Promise 的异常原因抛出。
另外,如果 await 操作符后的表达式的值不是一个 Promise,那么该值将被转换为一个已正常处理的 Promise。
简单来说:await 是一个运算符,等到了它要等的东西,一个Promise对象或者其他值,或者异常,这里有三种情况。
- 如果它等到的不是一个 Promise 对象,那 await 表达式的运算结果就是该表达式的值。
var y = await 20 * 5;
console.log(y); // 100
}
f2();
-
如果它等到的是一个 Promise 对象,await 就忙起来了,它会阻塞后面的代码,等着 Promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果。
function resolveAfter2Seconds(x) { return new Promise(resolve => { setTimeout(() => { resolve(x); }, 2000); }); } async function f1() { var x = await resolveAfter2Seconds(10); console.log(x); // 10 console.log(11)//10 输出后再输出的11 } f1();
-
如果await 后的 Promise 处理异常,则异常值被抛出。
Promise.reject(reason)方法返回一个用reason拒绝的Promise。
async function f3() {
try {
var z = await Promise.reject(30);
} catch (e) {
console.log(e); // 30
}
}
f3();
async/await 的使用
先做个比较
之前说用async 会将其后的函数返回值封装为一个promise对象。
现在用 setTimeout 模拟耗时的异步操作,先来看看不用 async/await 会怎么写:
function takeLongTime() {
return new Promise(resolve => {
setTimeout(() => resolve("long_time_value"), 1000);
});
}
takeLongTime().then(v => {
console.log("got", v);
});
用 async/await 呢,会是这样:
function takeLongTime() {
return new Promise(resolve => {
setTimeout(() => resolve("long_time_value"), 1000);
});
}
async function test() {
const v = await takeLongTime();
console.log(v);
}
test();
这两种方式对异步的处理并没有差别,name使用async的优势在哪?
async/await 的优势在于处理 then 链
如果需要处理由多个 Promise 组成的 then 链的时候:
假设一个业务,分多个步骤完成,每个步骤都是异步的,而且依赖于上一个步骤的结果。我们仍然用 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();
// 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 实现是一样的,但是代码结构明显更清晰。
现在把业务要求改一下,仍然是三个步骤,但每一个步骤都需要之前每个步骤的结果。
function step1(n) {
console.log(`step1 with ${n}`);
return takeLongTime(n);
}
function step2(m, n) {
console.log(`step2 with ${m} and ${n}`);
return takeLongTime(m + n);
}
function step3(k, m, n) {
console.log(`step3 with ${k}, ${m} and ${n}`);
return takeLongTime(k + m + n);
}
用 async/await 来写:
async function doIt() {
console.time("doIt");
const time1 = 300;
const time2 = await step1(time1);
const time3 = await step2(time1, time2);
const result = await step3(time1, time2, time3);
console.log(`result is ${result}`);
console.timeEnd("doIt");
}
doIt();
// c:\var\test>node --harmony_async_await .
// step1 with 300
// step2 with 800 = 300 + 500
// step3 with 1800 = 300 + 500 + 1000
// result is 2000
// doIt: 2907.387ms
Promise 方式:
function doIt() {
console.time("doIt");
const time1 = 300;
step1(time1)
.then(time2 => {
return step2(time1, time2)
.then(time3 => [time1, time2, time3]);
})
.then(times => {
const [time1, time2, time3] = times;
return step3(time1, time2, time3);
})
.then(result => {
console.log(`result is ${result}`);
console.timeEnd("doIt");
});
}
doIt();
结果一目了然。
参考文章: