锋利的ES6 — Async Await

  1. 概念
  2. 优点
  3. 用法
  4. 处理Ajax

锋利的ES6 系列文章介绍ES6(ES5+)中新增的比较好用的新特性。本篇聊聊async await。



首先,在阅读本篇文章之前你需要非常了解Promise,如果不了解,可以先移步我的另外一篇锋利的ES6 — Promise

概念

async函数其实是Generator的语法糖,详情可以参考锋利的ES6 — Generatorasync函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await,仅此而已。async函数可以看作多个异步操作,包装成的一个 Promise 对象,而await命令就是内部then命令的语法糖。

优点

  1. 内置执行器

    Generator的执行是分两步的,必须要先执行函数本身得到一个遍历器对象再通过yield来执行,而async函数自带执行器。也就是说,async函数的执行,与普通函数一模一样,只需要直接调用即可。

  2. 更好的语义化

    asyncawait,比起星号和yield,语义更清楚了。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。

  3. 更广的适用性

    async函数的await命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时会自动转成立即 resolved 的 Promise 对象)。

  4. 返回值是 Promise

    async函数的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象方便多了。你可以用then方法指定下一步的操作。

用法

先来看看一个简单的Promise的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
const pro = new Promise((resolve, reject) => {
setTimeout(() => {
const num = Math.random()
if (num > 0.5) resolve(num)
else reject(num)
}, 1000)
})

pro.then((n) => {
console.log(n + '大于0.5,承诺兑现')
}).catch((n) => {
console.log(n + '不大于0.5,承诺失信')
})

这种写法虽然不用写回调了,但是仍然需要在thencatch里面传入函数来处理,倒不是说Promise不好,只是async的写法更加舒服,简直跟同步写法没有区别。改造如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function foo () {
return new Promise((resolve, reject) => {
setTimeout(() => {
const num = Math.random()
if (num > 0.5) resolve(num)
else reject(num)
}, 1000)
})
}
async function bar () {
var num = await foo()
console.log(num + '大于0.5,承诺兑现')


}
bar()
  1. async修饰的函数内部才能使用await关键字
  2. await表达式后面的函数需要返回一个Promise对象
  3. await表达式的结果就是resolve传递过来的参数

但是,当你运行上面的代码的时候你会发现如果Promise走了reject,代码会报错,因为await只接收resolve的值不接收reject,因此我们可以把代码放进try...catch语句中,改造如下:

1
2
3
4
5
6
7
8
9
async function bar () {
try {
var num = await foo()
console.log(num + '大于0.5,承诺兑现')
} catch (n) {
console.log(n + '不大于0.5,承诺失信')
}

}

处理Ajax

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
28
29
30
31
32
33
function get (url, query, isJson = true) {
if (query) {
url += '?'
for (var key in query) {
url += `${key}=${query[key]}&`
}
url = url.slice(0, -1)
}
return new Promise((resolve, reject) => {
var xhr = new XMLHttpRequest()
xhr.open('get', url)
xhr.send()
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
resolve(isJson ? JSON.parse(xhr.responseText) : xhr.responseText)
} else {
reject()
}
}
}
})
}

async function getTodos () {
try {
const res = await get('https://jsonplaceholder.typicode.com/todos')
console.log(res)
} catch (e) {
throw(e)
}
}
getTodos()

也可以在一个async函数里使用多个await

1
2
3
4
5
6
7
8
9
10
async function getTodos () {
try {
const res1 = await get('https://jsonplaceholder.typicode.com/todos')
const res2 = await get('https://jsonplaceholder.typicode.com/todos')
console.log({ res1, res2 })
} catch (e) {
throw(e)
}
}
getTodos()

多个await会等到第一个await异步完成以后才会执行第二个await。任意一个异步失败都会进入catch

但是,这种写法的话两个异步代码会存在前后顺序关系,第一个异步resolve之后第二个才会开始发送,为了节约时间我们可以换一种写法:

1
2
3
4
5
6
7
8
async function getTodos () {
const [ res1, res2 ] = await Promise.all(
get('https://jsonplaceholder.typicode.com/todos'),
get('https://jsonplaceholder.typicode.com/todos')
)
console.log({ res1, res2 })
}
getTodos()

或者:

1
2
3
4
5
6
7
8
9
10
11
12
async function getTodos () {
try {
const pro1 = get('https://jsonplaceholder.typicode.com/todos')
const pro2 = get('https://jsonplaceholder.typicode.com/todos')
const res1 = await pro1
const res2 = await pro2
console.log({ res1, res2 })
} catch (e) {
throw(e)
}
}
getTodos()

这两种写法都是并行执行多个异步,这样就会缩短程序的执行时间。


代码不是万能的,但不写代码是万万不能的

Dary记


  • 更多干货,尽在公众号



转载请注明来源,文末有原始链接。欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 dary1112@foxmail.com

创作不易,您的打赏是我更新的动力

  • 支付宝

  • 微信

文章标题:锋利的ES6 — Async Await

文章字数:1.1k

本文作者:Dary

发布时间:2020-05-28, 18:51:00

最后更新:2020-05-28, 19:07:47

原始链接:http://www.xiongdalin.com/2020/05/28/ES6-async-await/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录