好久没写博客了,今天终于把拖了好久的一篇写完了。
原文地址:必须知道的 Promise 进阶点(二)
前面还有:必须知道的 Promise 进阶点(一)
async/await 基础
比起回调函数那看不懂的嵌套,Promise 要清爽不少,但当任务较多时, Promise 也有可能会有比较长的链和嵌套,这时候使用 async/await 就会让代码易读很多。
async
async 函数可以看作是 Promise 的语法糖:
// resolve 状态
async function foo() {
console.log('start')
return 'resolve'
}
foo().then(data => {
console.log('data:', data)
})
console.log('end')
// start
// end
// data: resolve
// reject状态
async function foo() {
console.log('start')
throw new Error('reject')
}
foo().catch(data => {
console.log('data:', data.message)
})
console.log('end')
// start
// end
// data: reject
与下面直接使用 Promise 方式书写的效果一模一样:
// resolve 状态
function foo() {
return new Promise((resolve, reject) => {
console.log('start')
resolve('resolve')
})
}
foo().then(data => {
console.log('data:', data)
})
console.log('end')
// start
// end
// data: resolve
// reject状态
function foo() {
return new Promise((resolve, reject) => {
console.log('start')
reject('reject')
})
}
foo().catch(data => {
console.log('data:', data)
})
console.log('end')
// start
// end
// data: reject
await
await 与其字面意思一样——等待,等待其后的函数执行完毕。
// resolve状态
async function foo() {
console.log('start')
return 'resolve'
}
async function bar() {
const data = await foo()
console.log('data', data)
}
bar()
console.log('end')
// start
// end
// data resolve
// reject状态
async function foo() {
console.log('start')
throw new Error('reject')
}
async function bar() {
try {
const data = await foo()
console.log('data', data)
} catch (err) {
console.log('data', err.message)
}
}
bar()
console.log('end')
// start
// end
// data reject
await 只能在 async 函数里使用,否则会报错
循环中的 async/await
首先思考下面两个场景:
-
有一个异步请求,需要多次并且按顺序发送
-
有一个异步请求,需要多次发送,但不用按顺序
场景一
同一个请求,多次,按顺序。这就是一个典型的串行处理
function mockServer(i) {
return new Promise((reslove, rejecy)=> {
setTimeout(() => {
reslove('有值了:' + i)
}, 1000 * i)
})
}
async function getData(time) {
var data = await mockServer(time)
return data
}
var arr = [1,2,3,4]
async function showData() {
console.time('showData')
for (const item of arr) {
const data = await getData(item)
console.log(data)
}
console.timeEnd('showData')
}
showData()
// 有值了: 1
// 有值了: 2
// 有值了: 3
// 有值了: 4
// howData: 13100.510009765625ms
我们通过 for-of
循环调用了 4 次异步函数 getData
,由于 getData
前面加了关键字 await
,所以会依次排队处理,一共花了13秒多的时间。
场景二
同一个请求,多次,不按顺序。这就是一个典型的并行处理,每个请求同时发送,而不用排队等候,节约时间。
function mockServer(i) {
return new Promise((reslove, rejecy)=> {
setTimeout(() => {
reslove('有值了:', i)
}, 1000 * i)
})
}
async function getData(time) {
var data = await mockServer(time)
return data
}
var arr = [1,2,3,4]
async function showData() {
console.time('showData')
var allAsyncRequest = arr.map(item => getData(item))
for await (const asyncRequest of allAsyncRequest) {
const data = asyncRequest
console.log(data)
}
console.timeEnd('showData')
}
showData()
// 有值了: 1
// 有值了: 2
// 有值了: 3
// 有值了: 4
// showData: 4131.318115234375ms
我们在 map 的回调里调用了 4 次异步请求函数,将请求事件放到事件队列里面,让 4 个请求可以同时处理,而不影响后续任务的执行。
然后再通过 for await...of
来等待 4 个异步请求都执行完,一共花了 4 秒,大大节约了时间。
这里的 for await...of
还可以换一种写法:
for (const asyncRequest of allAsyncRequest) {
const data = await asyncRequest
console.log(data)
}
也可以使用 Promise.all()
:
Promise.all(allAsyncRequest).then((data) => {
console.log(data)
console.timeEnd('showData')
})
// ["有值了:1", "有值了:2", "有值了:3", "有值了:4"]
// showData: 4441.679931640625ms
注意:这里不能使用
forEach
来进行循环处理,具体原因可以看 当async/await遇上forEach,这篇文章已经写的很清楚了。
最后
学会熟练使用 async/await,可以很好提升代码的可阅读和可维护性,大家如果还有更好的用法和建议,欢迎在评论区补充。