1、什么是async、await?
async顾名思义是“异步”的意思,async用于声明一个函数是异步的。而await从字面意思上是“等待”的意思,就是用于等待异步完成。并且await只能在async函数中使用。
通常async、await都是跟随Promise一起使用的。为什么这么说呢?因为async返回的都是一个Promise对象同时async适用于任何类型的函数上。这样await得到的就是一个Promise对象(如果不是Promise对象的话那async返回的是什么 就是什么);
await得到Promise对象之后就等待Promise接下来的resolve或者reject。
我们先看个例子:
var sleep = function (time) { return new Promise(function (resolve, reject) { setTimeout(function () { resolve(); }, time); }) }; var start = async function () { // 在这里使用起来就像同步代码那样直观 console.log('start'); await sleep(3000); console.log('end'); }; start();
上面代码控制台运行的结果是:先打印出‘start’,然后等待3秒后打印出“end”,这就是一个简单的async/await的例子。
2、我们需要知道的是:
1、async 表示这是一个async函数,await只能用在这个函数里面。 2、async/await 是一种编写异步代码的新方法。之前异步代码的方案是回调和 promise。 3、async/await 是建立在 promise 的基础上。 await 后面跟着的应该是一个promise对象(当然,其他返回值也没关系,只是会立即执行,不过那样就没有意义了…), await 表示在这里等待promise返回结果了,再继续执行。 4、async/await 像 promise 一样,也是非阻塞的。 5、async/await 让异步代码看起来、表现起来更像同步代码。这个是async/await 的真正威力,也可以说是其相比较promise的最大优点。
3、首先了解async函数起什么作用
async function test() { return "hello world"; } const result = test(); console.log(result);
浏览器打印出来的是:Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: "hello world"}
想一下,如果async函数没有返回值呢?会是什么样?如下代码:
async function test() { console.log("111111111") } const result = test(); console.log(result);
浏览器打印结果如下:
‘111111111’
Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: undefined}
也就是说结果返回的是Promise.resolve(undefined)
看到这里明白了吗?async 函数返回的是一个 Promise 对象。也就是说如果我们在async函数中 return 一个常量,async 会把这个常量通过 Promise.resolve() 封装成 Promise 对象。
既然是 Promise 对象,那我们也就可以通过then()方法获取其值,如下
test().then(res => { console.log(res); // 输出 hello world });
看了上面的代码,是不是很不明白async/await到底有啥用?
我们知道Promise 的特点——无等待,所以在没有 await 的情况下执行 async 函数,它会立即执行,返回一个 Promise 对象,并且,绝不会阻塞后面的语句。这和普通返回 Promise 对象的函数并无二致。
那么下一个关键点就在于 await 关键字了。
4、await 有啥用,做了什么?
await 操作符一般说来是用于等待一个Promise 对象。它只能在异步函数 async function 中使用。但事实是,await后可以是一个 Promise 对象或者任何要等待的值。请参见:await 语法。
如果一个 Promise 被传递给一个 await 操作符,await 将等待 Promise 正常处理完成并返回其处理结果。
function test(x) { return new Promise(resolve => { setTimeout(() => { resolve(x); }, 2000); }); } async function f1() { var x = await test(10); console.log(x); // 10 } f1();
如果该值不是一个 Promise,await 会把该值转换为已正常处理的Promise,然后等待其处理结果。
async function f2() { var y = await 20; console.log(y); // 20 } f2();
如果 Promise 处理异常,则异常值被抛出。
async function f3() { try { var z = await Promise.reject(30); } catch (e) { console.log(e); // 30 } } f3();
5、async/await 的优势在于处理 then 链
这个是使用async/await最优势的地方
单一的 Promise 链并不能发现 async/await 的优势,但是,如果需要处理由多个 Promise 组成的 then 链的时候,优势就能体现出来了(其实,Promise 通过 then 链来解决多层回调的问题,现在又用 async/await 来进一步优化它,也可以说是async/await基于Promise 做了更好的处理)
假设一个业务,分多个步骤完成,每个步骤都是异步的,而且依赖于上一个步骤的结果。我们仍然用 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 处理 then 链,网上有很多文章,可以搜搜,基本可以理解的差不多。