XingYun blog
  • JS基础

    • 图解js原型链
    • JS Event Loop
    • 对象的底层数据结构
    • 让你的JavaScript代码简单又高效
    • 函数参数按值传递
    • 判断数据类型
    • 浮点数精度问题和解决办法
    • 常用方法snippet
    • 实现Promise
    • 防抖和节流
    • 巧用sort排序
  • CSS && HTML

    • CSS也需要性能优化
    • class命名规范
    • em、px、rem、vh、vw 区别
    • CSS揭秘阅读笔记
  • 浏览器

    • 浏览器是如何渲染页面的
    • 重排和重绘
    • BOM浏览器对象模型
    • DOM事件
    • 浏览器存储
  • 数据结构

    • JS实现链表
    • JS实现栈与栈应用
    • JS实现常见排序
    • 哈夫曼编码
    • MD5算法
  • vue原理浅析

    • Vue虚拟dom与Diff算法
    • 前端打包文件的缓存机制
    • vue数组为什么不是响应式
    • v-for为什么不能用index做key
  • 前端工程化

    • 浏览器是如何渲染页面的
    • 前端打包需要gzip压缩吗
    • 前端打包文件的缓存机制
    • webpack loader和plugin
  • 轮子&&组件库

    • 实现水波浪进度球
  • 文字转语音mp3文件
  • 文件上传前后端实现
  • moment.js给定时间获取自然月、周的时间轴
  • 实现文件上传功能
  • 批量下载照片
  • leaflet改变坐标原点
  • 网络

    • 有了MAC地址 为什么还需要IP地址
    • 为什么IP地址老是变
    • 我们为什么需要IPV6
    • TCP与UDP
  • 计算机组成原理

    • ASCII、Unicode、UTF-8和UTF-16
  • VSCode

    • VSCode图片预览插件 Image preview
    • rsync:linux间的高效传输工具

XingYun

冲!
  • JS基础

    • 图解js原型链
    • JS Event Loop
    • 对象的底层数据结构
    • 让你的JavaScript代码简单又高效
    • 函数参数按值传递
    • 判断数据类型
    • 浮点数精度问题和解决办法
    • 常用方法snippet
    • 实现Promise
    • 防抖和节流
    • 巧用sort排序
  • CSS && HTML

    • CSS也需要性能优化
    • class命名规范
    • em、px、rem、vh、vw 区别
    • CSS揭秘阅读笔记
  • 浏览器

    • 浏览器是如何渲染页面的
    • 重排和重绘
    • BOM浏览器对象模型
    • DOM事件
    • 浏览器存储
  • 数据结构

    • JS实现链表
    • JS实现栈与栈应用
    • JS实现常见排序
    • 哈夫曼编码
    • MD5算法
  • vue原理浅析

    • Vue虚拟dom与Diff算法
    • 前端打包文件的缓存机制
    • vue数组为什么不是响应式
    • v-for为什么不能用index做key
  • 前端工程化

    • 浏览器是如何渲染页面的
    • 前端打包需要gzip压缩吗
    • 前端打包文件的缓存机制
    • webpack loader和plugin
  • 轮子&&组件库

    • 实现水波浪进度球
  • 文字转语音mp3文件
  • 文件上传前后端实现
  • moment.js给定时间获取自然月、周的时间轴
  • 实现文件上传功能
  • 批量下载照片
  • leaflet改变坐标原点
  • 网络

    • 有了MAC地址 为什么还需要IP地址
    • 为什么IP地址老是变
    • 我们为什么需要IPV6
    • TCP与UDP
  • 计算机组成原理

    • ASCII、Unicode、UTF-8和UTF-16
  • VSCode

    • VSCode图片预览插件 Image preview
    • rsync:linux间的高效传输工具
  • leaflet改变坐标原点
  • 补间动画gsap与tween
  • 文字转语音mp3文件
  • JavaScript引入
  • JavaScript高级程序设计阅读笔记
  • Javascript函数参数按值传递传递
  • JS防抖和节流
  • 手写JS常用方法
  • 手写Promise
  • JS Event Loop
  • 重排和重绘
  • em、px、rem、vh、vw 区别
  • css也需要性能优化
  • 图解 js 原型链
  • js函数参数按值传递
  • BOM浏览器对象模型
  • DOM
  • 事件
  • js对象数组sort按需排序
  • 文件上传功能技术选型和前后端实现
  • 前端图片处理
  • 让你的JavaScript代码简单又高效
  • BEM:class命名规范
  • 前端规范-CSS属性那么多(杂),怎么排序
  • TypeScript Tips
  • jsx
  • canvas基础
  • 前端日志
  • 浏览器存储
  • CSS世界阅读笔记
  • CSS揭秘阅读笔记
  • js变量命名常用规范词
  • 你不知道的JavaScript阅读笔记
  • js对象的底层数据结构
  • js判断数据类型
  • JS浮点数精度问题和解决办法
  • js作用域
  • js堆栈溢出和内存泄漏
  • 浏览器是如何渲染页面的
  • 疑难杂症和踩坑问题合集
  • 免费在线API收集
  • 原生JS实现Ajax请求
  • cookie、session、localStorage、sessionStorage的区别
  • Sass与Less
  • arrayBuffer、blob、file对象
  • TypeScript基础
  • 前端
XingYun
2022-12-05
目录

async异步原理

为了了解 Async / Await 的原理, 需要了解一些前置知识

# 一、Generator 与 yield

# 1.1 基本使用方法

Generator(生成器)是 ES6 中的关键词,通俗来讲 Generator 是一个带星号的函数(它并不是真正的函数),可以配合 yield 关键字来 暂停 或者 执行函数。先来看一个例子:

function* gen() {
  console.log('enter')
  let a = yield 1
  let b = yield (function () {
    return 2
  })()
  return 3
}
var g = gen() // 阻塞,不会执行任何语句
console.log(typeof g) // 返回 object 这里不是 "function"
console.log(g.next())
console.log(g.next())
console.log(g.next())
console.log(g.next())
1
2
3
4
5
6
7
8
9
10
11
12
13
14
object
enter
{ value: 1, done: false }
{ value: 2, done: false }
{ value: 3, done: true }
{ value: undefined, done: true }
1
2
3
4
5
6

Generator 中配合使用 yield 关键词可以控制函数执行的顺序,每当执行一次 next 方法,Generator 函数会执行到下一个存在 yield 关键词的位置。
总结,Generator 的执行的关键点如下:

  • 调用 gen() 后,程序会阻塞,不会执行任何语句;
  • 调用 g.next() 后,程序继续执行,直到遇到 yield 关键词时执行暂停;
  • 一直执行 next 方法,最后返回一个对象,其存在两个属性:value  和  done。

# 1.2 原理

其实,在生成器内部,如果遇到 yield 关键字,那么 V8 引擎将返回关键字后面的内容给外部,并暂停该生成器函数的执行。生成器暂停执行后,外部的代码便开始执行,外部代码如果想要恢复生成器的执行,可以使用 result.next 方法。

谷歌 V8 为了实现生成器函数的暂停执行和恢复执行的, 它用到了协程,协程是—种比线程更加轻量级的存在。可以把协程看成是跑在线程上的任务,一个线程上可以存在多个协程,但是在线程上同时只能执行一个协程。比如,当前执行的是 A 协程,要启动 B 协程,那么 A 协程就需要将主线程的控制权交给 B 协程,这就体现在 A 协程暂停执行,B 协程恢复执行; 同样,也可以从 B 协程中启动 A 协程。通常,如果从 A 协程启动 B 协程,我们就把 A 协程称为 B 协程的父协程。

正如一个进程可以拥有多个线程一样,一个线程也可以拥有多个协程。每一时刻,该线程只能执行其中某一个协程。最重要的是,协程不是被操作系统内核所管理,而完全是由程序所控制(也就是在用户态执行)。这样带来的好处就是性能得到了很大的提升,不会像线程切换那样消耗资源。

# 二、 Async / Await

ES7 新增了两个关键字: async 和 await, 是 Generator 的语法糖.

使用 await 关键字可以暂停异步代码的执行,等待 Promise 解决。

async 关键字可以让函数具有异步特征,但总体上代码仍然是同步求值的。

它们的用法很简单,首先用 async 关键字声明一个异步函数:

async function httpRequest() {}
1

然后就可以在这个函数内部使用 await 关键字了:

async function httpRequest() {
  let res1 = await httpPromise(url1)
  console.log(res1)
}
1
2
3
4

上述例子, await 关键字并不会导致程序阻塞,代码仍然是异步的,而 await 只是掩盖了这个事实,这就意味着任何使用 await 的代码本身都是异步的。

可以看出,async 函数返回的是 Promise 对象 。如果异步函数使用 return 关键字返回了值(如果没有 return 则会返回 undefined),这个值则会被 Promise.resolve() 包装成 Promise 对象。 异步函数始终返回 Promise 对象 。

# await 可以用于 非 Promise 对象吗?

可以

function getSomething() {
  return 'something'
}
async function testAsync() {
  return Promise.resolve('hello async')
}
async function test() {
  const v1 = await getSomething()
  const v2 = await testAsync()
  console.log(v1, v2)
}
test()

// something
// hello async
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  • 如果 await 的不是一个 Promise 对象,那 await 表达式的运算结果就是 await 的内容;
  • 如果 await 的是一个 Promise 对象,await 就就会阻塞后面的代码,等着 Promise 对象 resolve,然后将得到的值作为 await 表达式的运算结果。

# 错误处理

看以下代码 控制台执行输出什么?

function httpPromise(url) {
  return new Promise((resolve, reject) => {
    reject(new Error('发生错误'))
  })
}

const caller = async function () {
  let res = await httpPromise()
  if (res) {
    console.log(222, res)
  } else {
    console.log(333, res)
  }
}

caller()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

直接报错了, 因为代码执行到

let res = await httpPromise()
1

之后接收到一个 Error 就退出了, 后面的代码也不执行了

所以更好的处理方式是

function httpPromise(url) {
  return new Promise((resolve, reject) => {
    reject(new Error('发生错误'))
  })
}

const caller = async function () {
  let res = await httpPromise().catch((err) => {
    console.log(111, err)
  })
  if (res) {
    // 这里写异步任务成功
    console.log(222, res)
  } else {
    // 这里写异步任务失败的逻辑
    console.log(333, res)
  }
}

caller()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

可以看出

let res = await httpPromise().catch((err) => {
  console.log(111, err)
})
1
2
3

错误在这一步被 catch 住之后, 后面的业务逻辑可以运行, 控制台也不报红了, 更 优雅

上次更新: 2023/04/05, 09:41:10
最近更新
01
JavaScript-test
07-20
02
二维码的原理
07-20
03
利用ChatGPT优化代码
07-20
更多文章>
Theme by Vdoing | Copyright © 2021-2023 XingYun | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式