AI实践哪家强?来 AICon, 解锁技术前沿,探寻产业新机! 了解详情
写点什么

编写高质量可维护的代码:异步优化

  • 2021-07-31
  • 本文字数:3634 字

    阅读完需:约 12 分钟

编写高质量可维护的代码:异步优化

前言


在现在前端开发中,异步操作的频次已经越来越高了,特别对于数据接口请求和定时器的使用,使得我们不得不关注异步在业务中碰到的场景,以及对异步的优化。错误的异步处理可能会带来很多问题,诸如页面渲染、重复加载等问题。


下面我们就先简单的从 JavaScript 中有大致的哪几种异步类型为切入点,然后再列举一些业务中我们会碰到的场景来逐个分析下,我们该如何解决。


异步实现种类


首先关于异步实现的方式上大致有如下几种:


callback


callback 即回调函数。这家伙出现很早很早了,他其实是处理异步的基本方法。并且回调的概念不单单出现在 JavaScript,你也会在 Java 或者 C# 等后端语言中也能找到他的影子。


回调函数简单的说其实就是给另外一个寄主函数作为传参的函数。在寄主函数执行完成或者执行到特定阶段之后触发调用回调函数并执行,然后把执行结果再返回给寄主函数的过程。


比如我们熟悉的 setTimeout 或者 React 中的 setState 的第二个方法都是以回调函数方式去解决异步的实现。


setTimeout(() => {   //等待0.2s之后再做具体的业务操作   this.doSomething();}, 200);this.setState({  count: res.count,}, () => {  //在更新完count之后再做具体的业务操作  this.doSomething();});
复制代码

Promise


Promise 是个好东西,有了它之后我们可以对异步进行很多操作,并且可以把异步以链式的方式进行操作。


其实在 JQuery 中的 deferred 和它就有点像,都是采用回调函数的解决方案,都可以做链式调用,但是在 Promise 中增加了错误的 catch 方法可以更加方便的处理异常场景,并且它内置状态(resolve, reject,pending),状态只能由 pending 变为另外两种的其中一种,且改变后不可逆也不可再度修改。


let promise = new Promise((resolve, reject) => {   reject("对不起,你不是我的菜");});promise.then((data) => {console.log('第一次success' + data);  return '第一次success' + data},(error) => {console.log(error) }).then((data2) => {  console.log('第二次success' + data2);},(error2) => {   console.log(error2) }).catch((e) => {  console.log('抓到错误啦' + e);});
复制代码

await/async


await/async 其实是 Promise 的一种升级版本,使用 await/async 调用异步的时候是从上到下,顺序执行,就像在写同步代码一样,这更加的符合我们编写代码的习惯和思维逻辑,所以容易理解。整体代码逻辑也会更加的清晰。


async function asyncDemoFn() {  const data1 = await getData1();  const data2 = await getData2(data1);  const data3 =  await getData3(data2);  console.log(data3)}await asyncDemoFn()
复制代码

generator


generator 中文名叫构造器,是 ES6 中的一个新东西,我相信很多人在现实的代码中很少能接触到它,所以它相对而言对大家来说还是比较晦涩,但是这家伙还是很强的,简单来说它能控制异步调用,并且其实是一个状态机。


function* foo() {  for (let i = 1; i <= 3; i++) {    let x = yield `等我一下呗,i = ${i}`;    console.log(x);  }}setTimeout(() => {  console.log('终于轮到我了');}, 1);var a = foo();console.log(a); // foo {<closed>}var b = a.next();console.log(b); // {value: "等我一下呗,i = 1", done: false}var c = a.next();console.log(c); // {value: "等我一下呗,i = 2", done: false}var d = a.next();console.log(d); // {value: "等我一下呗,i = 3", done: false}var e = a.next();console.log(e); // {value: undefined, done: true}// 终于轮到我了
复制代码


上面代码的函数 foo 是一个协程,它的厉害的地方就是 yield 命令。它表示执行到此处,执行权将交给其他协程。也就是说,yield 命令是异步两个阶段的分界线。


协程遇到 yield 命令就暂停,等到执行权返回,再从暂停的地方继续往后执行。它的最大优点,就是代码的写法非常像同步操作,如果去除 yield 命令,简直一模一样。


再来个有点贴近点场景方式来使用下 generator。比如现在在页面中我们需要自动的执行 checkAuth 和 checkAddress 检查,我们就用 generator 的方式去实现自动检查上述两异步检查。


const checkAuth = () => {    return new Promise((resolve)=>{        setTimeout(()=>{           resolve('checkAuth1')         },1000)    })}const checkAddress = () => {    return new Promise((resolve)=>{        setTimeout(()=>{            resolve('checkAddress2')        },2000)    })}var steps = [checkAuth,checkAddress]function* foo(checkList) {  for (let i = 0; i < checkList.length; i++) {    let x = yield checkList[i]();    console.log(x);  }}var stepsGen = foo(steps)var run = async (gen)=>{    var isFinnish = false    do{       const {done,value} = gen.next()       console.log('done:',done)       console.log('value:',value)       const result = await value       console.log('result:',result)              isFinnish = done    }while(!isFinnish)    console.log('isFinnish:',isFinnish)}run(stepsGen)
复制代码


种类对比


  • 从时间维度从早到晚:callback,Promise,generator,await/async

  • await/async 是目前对于异步的终极形式

  • callback 让我们有了基本的方式去处理异步情况,Promise 告别了 callback 的回调地狱并且增加 resolve,reject 和 catch 等方法让我们能处理不同的情况,generator 增加了对于异步的可操作性,类似一个状态机可暂时停住多个异步的执行,然后在合适的时候继续执行剩余的异步调用,await/async 让异步调用更加语义化,并且自动执行异步


异步业务中碰到的场景

回调地狱


在使用回调函数的时候我们可能会有这样的场景,B 需要在 A 的返回之后再继续调用,所以在这样有先后关系的时候就存在了一个叫回调地狱的问题了。


getData1().then((resData1) => {  getData2(resData1).then((resData2) => {    getData3(resData2).then((resData3)=>{      console.log('resData3:', resData3)    })  });});
复制代码


碰到这样的情况我们可以试着用 await/async 方式去解这种有多个深层嵌套的问题。


async function asyncDemoFn2() {  const resData1 = await getData1();  const resData2 = await getData2(resData1);  const resData3 =  await getData3(resData2);  console.log(resData3)}await asyncDemoFn2()
复制代码


异步循环


在业务中我们最最经常碰到的就是其实还是存在多个异步调用的顺序问题,大致上可以分为如下几种:


并行执行


在并行执行的时候,我们可以直接使用 Promise 的 all 方法


Promise.all([getData1(),getData2(),getData3()]).then(res={console.log('res:',res)})
复制代码


顺序执行


在顺序执行中,我们可以有如下的两种方式去做


  1. 使用 async/await 配合 for

const sources = [getData1,getData2,getData3]async function promiseQueue() {  console.log('开始');  for (let targetSource in sources) {    await targetSource();  }  console.log('完成');};promiseQueue()
复制代码


  1. 使用 async/await 配合 while

// getData1,getData2,getData3 都为 promise 对象const sources = [getData1,getData2,getData3]async function promiseQueue() {  let index = 0  console.log('开始');  while(index >=0 && index < sources.length){    await targetSource();    index++  }  console.log('完成');};promiseQueue()
复制代码


  1. 使用 async/await 配合 reduce

// getData1,getData2,getData3 都为 promise 对象const sources = [getData1,getData2,getData3]sources.reduce(async (previousValue, currentValue)=>{  await previousValue  return currentValue()},Promise.resolve())
复制代码


  1. 使用递归


const sources = [getData1,getData2,getData3]function promiseQueue(list , index = 0) {  const len = list.length  console.log('开始');  if(index >= 0 && index < len){    list[index]().then(()=>{      promiseQueue(list, index+1)          })  }  console.log('完成');}promiseQueue(sources)
复制代码


结尾


今天只是关于异步的普通使用场景的讨论,并且做了些简单的例子。其实关于异步的使用还有很多很多复杂的使用场景。更多的奇思妙想正等着你。


头图:Unsplash

作者:毅轩

原文:https://mp.weixin.qq.com/s/s6fVoY31MqUXrW8RPka3pA

原文:编写高质量可维护的代码:异步优化

来源:政采云前端团队 - 微信公众号 [ID:Zoo-Team]

转载:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

2021-07-31 10:002409

评论

发布
暂无评论
发现更多内容

Python数据结构:字典详解(创建、访问、修改、字典方法)

我再BUG界嘎嘎乱杀

Python 编程 数据结构 后端 开发语言

【YashanDB知识库】存储过程报错snapshot too old

YashanDB

yashandb 崖山数据库 崖山DB

Cloud Kernel SIG 月度动态:发布 ANCK 3 个版本,5.10 kABI/kAPI 策略变更

OpenAnolis小助手

操作系统 龙蜥社区 龙蜥社区SIG Cloud Kernel

玩转 Easysearch 语法

极限实验室

数据库 搜索引擎 easysearch 极限科技 征文系列

2024中国PMO高峰论坛在京成功召开

财见

数据可视化在石油新能源行业的应用:深度探索与前沿趋势

不在线第一只蜗牛

数据挖掘 数据分析 低代码 数据可视化

阶跃星辰启动「繁星计划」开放平台;运动迁移框架 MotionClone 无需训练,一键克隆视频运动丨 RTE 开发者日报

声网

腾讯特别调薪8%,年底十三薪分摊到月薪:福利升级还是另有深意?

王中阳Go

腾讯 面经

企业全历史行为数据助ToB企业决策层开启营销的上帝视角

客户在哪儿AI

ToB营销 ToB增长 ToB销售

线上观看 3 万+!「智能可观测运维技术MeetUp」精彩回顾,探讨智能体构建新方向

OpenAnolis小助手

操作系统 龙蜥社区 龙蜥meetup 可观测技术

高性能网络SIG月度动态:virtio技术委员会通过flow director提案,netdim调节特性正式合入上游社区

OpenAnolis小助手

操作系统 龙蜥社区 龙蜥社区SIG

说说RabbitMQ延迟队列实现原理?

王磊

Java 面试

本周五开讲!AI 时代的运维开发工具 OS Copilot 陪跑班,分享云上最佳实践案例

OpenAnolis小助手

Alibaba Cloud Linux OS Copilot 运维开发工具

JDBC 最佳实践

FunTester

优秀Java 开发者都在参与的项目

XIAOJUSURVEY

maven 服务端 springboot Java 8

“Pandabuy事件后,淘宝代购集运系统如何强化仿牌敏感词风控策略“

tbapi

淘宝代购集运系统 Pandabuy 逆向海淘系统

怎么填充PPT底色?分享2个办公必备的PPT技巧!

彭宏豪95

职场 PPT PPT模板 办公软件 AI生成PPT

高性能存储 SIG 月度动态:优化 xfs dax reflink 时延,独立选型并维护 mdadm 和 ledmon

OpenAnolis小助手

操作系统 高性能存储 龙蜥社区SIG

破局移动影像,华为的化境是绝无止境

脑极体

AI

22 位委员参会,第 25 次龙蜥社区运营委员会圆满结束

OpenAnolis小助手

操作系统 龙蜥社区

Python的众多包管理器

我再BUG界嘎嘎乱杀

Python 编程 后端 开发语言

阿里云大牛熬夜整理的Python大数据小抄,GitHub星标125K!

我再BUG界嘎嘎乱杀

Python 大数据 编程 后端 开发语言

Databend 完美适配 KubeSphere 企业版 4.1.1,让云原生技术更普及

Databend

轻松应用 RapidMiner 内置案例模板实现数据挖掘详解(下篇)

Altair RapidMiner

人工智能 数据挖掘 算法 数据分析 altair

苏州八大行业服务器托管方案分享?IDC机房选择经验

苏州服务器托管

算力 IDC 服务器托管

“SelectDB 实时数据仓库解决方案”入围工信部“信息技术应用创新典型解决方案”

SelectDB

数据库 大数据 数据仓库 云原生 信创

用这2款AIPPT软件,让你的Markdown生成PPT!

彭宏豪95

人工智能 PPT 在线白板 AIGC AI生成PPT

告别 CentOS,开源操作系统与时代同步更需“根”的力量

OpenAnolis小助手

操作系统 龙蜥社区 CentOS 停服

编写高质量可维护的代码:异步优化_语言 & 开发_政采云前端团队_InfoQ精选文章