async/await

589 阅读3分钟

ES7中新增的“语法糖”:async await

async 是“异步”的简写,而 await 可以认为是 async wait 的简写。所以应该很好理解 async 用于申明一个 function 是异步的,而 await 用于等待一个异步方法执行完成。

  • =>函数中只要使用await,则当前函数必须使用async修饰
  • async是修饰一个函数,控制其返回的结果是一个Promise实例
  • await可以理解为把一个异步操作修饰为同步的效果(但是它还是异步)

1. async 起什么作用

async function func() {
	return 100;  
}
let res = func();
console.log(res); 

//输出的是一个 Promise 对象。

Promise {<resolved>: 100}

所以,async 函数返回的是一个 Promise 对象。如果在函数中 return 一个直接量,async 会把这个直接量通过 Promise.resolve() 封装成 Promise 对象。

async 函数返回的是一个 Promise 对象,所以在最外层不能用 await 获取其返回值的情况下,我们当然应该用原来的方式:then() 链来处理这个 Promise 对象,就像这样

func().then( x => {
    console.log(x)
}

如果 async 函数没有返回值,它会返回 Promise.resolve(undefined)。

联想一下 Promise 的特点——无等待,所以在没有 await 的情况下执行 async 函数,它会立即执行,返回一个 Promise 对象,并且,绝不会阻塞后面的语句。这和普通返回 Promise 对象的函数一样。

2. await 到底在等啥?

因为 async 函数返回一个 Promise 对象,所以 await 可以用于等待一个 async 函数的返回值——这也可以说是 await 在等 async 函数,但要清楚,它等的实际是一个返回值。注意到 await 不仅仅用于等 Promise 对象,它可以等任意表达式的结果,所以,await 后面实际是可以接普通函数调用或者直接量的。

function func() {
    return "func";
}

async function func1() {
    return Promise.resolve("hello~");
}

async function test() {
    const x1 = await func();
    const x2 = await func1();
    console.log(x1, x2);
}

test();

3. await 等到想等的

  • 如果它等到的不是一个 Promise 对象,那 await 表达式的运算结果就是它等到的东西。

  • 如果它等到的是一个 Promise 对象,await 就忙起来了,它会阻塞后面的代码,等着 Promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果。

  • 这就是 await 必须用在 async 函数中的原因。async 函数调用不会造成阻塞,它内部所有的阻塞都被封装在一个 Promise 对象中异步执行。

async/await 帮我们干了啥

1.上面已经说明了 async 会将其后的函数的返回值封装成一个 Promise 对象,而 await 会等待这个 Promise 完成,并将其 resolve 的结果返回出来。

先看看不用 async/await 会怎么写:

let promise1 = new Promise(resolve => {
	setTimeout(() => {
		resolve(1);
	}, 1000);
});
promise1.then(res => {
	console.log(res);
});

使用async/await 会怎么写:

let promise1 = new Promise(resolve => {
	setTimeout(() => {
		resolve(1);
	}, 1000);
});

async function func(){
    let x = await promise1;
    console.log(x);
};
func();

2.async/await 的优势在于处理 then 链

单一的 Promise 链并不能发现 async/await 的优势,但是,如果需要处理由多个 Promise 组成的 then 链的时候,优势就能体现出来了(很有意思,Promise 通过 then 链来解决多层回调的问题,现在又用 async/await 来进一步优化它)

缺点在于滥用 await 可能会导致性能问题,因为 await 会阻塞代码,也许之后的异步代码并不依赖于前者,但仍然需要等待前者完成,导致代码失去了并发性。

/**
 * 传入参数 n,表示这个函数执行的时间(毫秒)
 * 执行的结果是 n + 100,这个值将用于下一步骤
 */
function func(n) {
    return new Promise(resolve => {
        setTimeout(() => resolve(n + 100), n);
    });
}

function test1(n) {
    console.log(`test1 === ${n}`);
    return func(n);
}

function test2(n) {
    console.log(`test2 === ${n}`);
    return func(n);
}

function test3(n) {
    console.log(`test3 === ${n}`);
    return func(n);
}

用promise来实现这些步骤:

function time() {
    console.time("start");
    const time1 = 100;
    test1(time1)
        .then(time2 => test2(time2))
        .then(time3 => test3(time3))
        .then(result => {
            console.log(`result is ${result}`);
            console.timeEnd("endding");
        });
}

time();

如果用 async/await 来实现呢,会是这样

async function time() {
    console.time("start");
    const time1 = 300;
    const time2 = await test1(time1);
    const time3 = await test2(time2);
    const result = await test3(time3);
    console.log(`result is ${result}`);
    console.timeEnd("endding");
}

time();