Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

必须知道的 Promise 进阶点(二) #23

Open
@spivet

Description

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

首先思考下面两个场景:

  1. 有一个异步请求,需要多次并且按顺序发送

  2. 有一个异步请求,需要多次发送,但不用按顺序

场景一

同一个请求,多次,按顺序。这就是一个典型的串行处理

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,可以很好提升代码的可阅读和可维护性,大家如果还有更好的用法和建议,欢迎在评论区补充。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

        AltStyle によって変換されたページ (->オリジナル) /