5
头图
"Code tailor",为前端开发者提供技术相关资讯以及系列基础文章,微信关注“小和山的菜鸟们”公众号,及时获取最新文章。

前言

在开始学习之前,我们想要告诉您的是,本文章是对阮一峰《ECMAScript6 入门》一书中 "async 和 await" 章节的总结,如果您已掌握下面知识事项,则可跳过此环节直接进入题目练习

  • 出现原由
  • 什么是 async ?
  • 什么是 await ?
  • 如何创建和使用?

如果您对某些部分有些遗忘,👇🏻 已经为您准备好了!

学习链接

async 和 await 的学习

汇总总结

出现原由

async functionsawait 关键字是最近添加到 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 模块
  • 更广的适用性。 asyncawait 后面跟的都是 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。仅当某个值成功(或拒绝)并且调用堆栈为空时,我才想使用该值。 我们可以在异步函数中同时使用.thenawait关键字来获取此值。尽管我们可以同时使用.thenawait来获得承诺的价值,但它们的工作方式略有不同。 在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'
  • 遇到setTimeoutconsole.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'

小和山的菜鸟们
377 声望2.1k 粉丝

每日进步的菜鸟,分享前端学习手册,和有心学习前端技术的小伙伴们互相探讨,一同成长。