"Code tailor",为前端开发者提供技术相关资讯以及系列基础文章,微信关注“小和山的菜鸟们”公众号,及时获取最新文章。
前言
在开始学习之前,我们想要告诉您的是,本文章是对阮一峰《ECMAScript6 入门》一书中 "async 和 await" 章节的总结,如果您已掌握下面知识事项,则可跳过此环节直接进入题目练习
- 出现原由
- 什么是 async ?
- 什么是 await ?
- 如何创建和使用?
如果您对某些部分有些遗忘,👇🏻 已经为您准备好了!
学习链接
汇总总结
出现原由
async functions
和await
关键字是最近添加到JavaScript
语言里面的。它们是ECMAScript 2017 JavaScript
版的一部分(参见 ECMAScript Next support in Mozilla )。简单来说,它们是基于promises
的语法糖,使异步代码更易于编写和阅读。通过使用它们,异步代码看起来更像是老式同步代码,因此它们非常值得学习。
什么是 async
async 函数是 AsyncFunction· 构造函数的实例, 并且其中允许使用await
关键字。
当使用 async
关键字,置于函数声明之前,使其成为 async function 。 异步函数是一个知道怎样使用 await
关键字调用异步代码的函数。
语法
async function name([param[, param[, ... param]]]) {
statements
}
参数
name
:函数名称
param
:传递给函数参数的名称
statements
:包含函数主体的表达式,可使用 await
机制
返回值
promise
这个 promise
要么会通过一个由 async
函数返回的值被解决,要么会通过一个从 async
函数中抛出的(或其中没有被捕获到的)异常被拒绝。
什么是 await
await
操作符用于等待一个Promise
对象。它只能在异步函数async function
中使用。
await
表达式会暂停当前 async function
的执行,等待 Promise
处理完成。若 Promise
正常处理( fulfilled
),其回调的 resolve
函数参数作为 await
表达式的值,继续执行 async function
。若 Promise
处理异常,则抛出错误原因
语法
;[返回值] = await 表达式
表达式
一个 Promise
对象或者任何要等待的值。
返回值
返回 Promise
对象的处理结果。如果等待的不是 Promise
对象,则返回该值本身。
创建和使用
让我们将上节的 readFile 示例
转化为使用 async / await
看看它使事情变得简单了多少
const readFile = function (name, ms) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(name + '读完了')
resolve()
}, ms)
})
}
async function useAsyncAwait() {
await readFile('first', 1000)
await readFile('second', 2000)
await readFile('third', 3000)
await readFile('forth', 4000)
await readFile('fifth', 5000)
console.log('async文件阅读完毕')
}
useAsyncAwait()
优点
- 内置执行器。意味着不需要像
generator
一样调用next
函数或co
模块 - 更广的适用性。
async
和await
后面跟的都是promise
函数,原始数据类型会被转为promise
- 语义更清晰、简洁
缺点
- 大量的 await 代码会阻塞(程序并不会等在原地,而是继续事件循环,等到响应后继续往下走)程序运行,每个 await 都会等待前一个完成
更多细节请参考JavaScript 异步发展史
题目自测
一:下列代码输出结果为()
const myPromise = () => Promise.resolve('I have resolved!')
function firstFunction() {
myPromise().then((res) => console.log(res))
console.log('second')
}
async function secondFunction() {
console.log(await myPromise())
console.log('second')
}
firstFunction()
secondFunction()
- A:
I have resolved!, second and I have resolved!, second
- B:
second, I have resolved! and second, I have resolved!
- C:
I have resolved!, second and second, I have resolved!
- D:
second, I have resolved! and I have resolved!, second
二:以下代码输出为()
async function getData() {
return await Promise.resolve('I made it!')
}
const data = getData()
console.log(data)
- A:
"I made it!"
- B:
Promise {<resolved>: "I made it!"}
- C:
Promise {<pending>}
- D:
undefined
三:以下代码输出什么?
async function async1() {
console.log('async1 start')
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2')
}
console.log('script start')
setTimeout(function () {
console.log('setTimeout')
}, 0)
async1()
new Promise(function (resolve) {
console.log('promise1')
resolve()
}).then(function () {
console.log('promise2')
})
console.log('script end')
题目解析
一、
Answer: D
Promise
。仅当某个值成功(或拒绝)并且调用堆栈为空时,我才想使用该值。 我们可以在异步函数中同时使用.then
和await
关键字来获取此值。尽管我们可以同时使用.then
和await
来获得承诺的价值,但它们的工作方式略有不同。 在firstFunction
中,我们(某种程度上)将myPromise
函数在运行时放在一边,但继续运行其他代码,在本例中为console.log('second')
。然后,该函数使用我已解析的字符串解析,然后在看到调用堆栈为空之后将其记录下来。 使用secondFunction
中的await
关键字,我们从字面上暂停了异步函数的执行,直到值解析完毕,然后再移至下一行。 这意味着它等待myPromise
解析为我已解析的值,只有发生这种情况后,我们才移至下一行:记录了第二行。
二、
Answer: A
异步函数总是返回一个承诺。等待仍然需要等待要解决的承诺:调用getData()
时返回挂起的承诺,以便将数据设置为它。
如果我们想访问解析的值“我做到了”,我们可以在数据上使用.then()
方法:
data.then(res => console.log(res))
会打印"I made it!"
三、
Answer:
// script start
// async1 start
// async2
// promise1
// script end
// async1 end
// promise2
// setTimeout
- 首先,事件循环从宏任务(
macrostack
)队列开始,这个时候,宏任务队列中,只有一个script
(整体代码)任务。从宏任务队列中取出一个任务来执行。 - 首先执行
console.log('script start')
,输出 ‘script start' - 遇到
setTimeout
把console.log('setTimeout')
放到macrotask
队列中 - 执行
aync1()
输出‘async1 start'
和'async2'
,把console.log('async1 end')
放到micro
队列中 - 执行到
promise
,输出'promise1'
,把console.log('promise2')
放到micro
队列中 - 执行
console.log('script end')
,输出 ‘script end' - macrotask 执行完成会执行
microtask
,把microtask quene
里面的microtask
全部拿出来一次性执行完,所以会输出'async1 end'
和‘promise2'
- 开始新一轮的事件循环,去除执行一个
macrotask
执行,所以会输出‘setTimeout'
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。