当promise遇上generator该如何应对?记一次工作中遇到的问题

news2024/12/26 11:07:17

问题背景

我们项目中有个保存功能,但是这个保存是一个异步函数,内部很多逻辑,比如说校验表单数据,获取子组件数据,数据处理,数据提交给后端获取中间值,最后保存。说明一下,我们的项目是vue2项目,下面的方法其实都在methoths里面this指向vue实例,大概逻辑是这样的,下面是简化逻辑:

// 入口函数,页面点击提交按钮执行的函数
aysnc function submitHandel() {
  await saveData()
  // 提示保存成功,并且刷新列表
  this.showAlart('保存成功')
  this.queryList()
}
// 保存函数
async function saveData(){
  // 校验组件a表单数据
  const {checkA, resultA} = this.$refs.comA.checkData()
  if (!checkA) return Promise.reject()
  // 校验组件b表单数据
  const {checkB, resultB} = this.$refs.comB.checkData()
  if (!checkB) return Promise.reject()
  // 处理数据
  const params = transactionData(resultA, resultB)
  // 一堆其他处理,里面还有异步代码,最后保存提交
  await saveApi(params)
}

整体逻辑大概就是上面这样的。结果在经历N多个迭代后,产品突然提了一个奇葩的需求,需要在保存过程中,增加一层校验,然后弹框告诉用户结果,然后用户只有点击确定后,才能保存数据。大概流程图和原型如下:
流程图原型

上面的需求导致一个很明显的结果,就是我们上面一部分函数的逻辑要挪到弹框里面的确认按钮之后了。改造后大概逻辑如下:

async function saveData(){
  // 校验组件a表单数据
  const {checkA, resultA} = this.$refs.comA.checkData()
  if (!checkA) return Promise.reject()
  // 校验组件b表单数据
  const {checkB, resultB} = this.$refs.comB.checkData()
  if (!checkB) return Promise.reject()
  // 处理数据
  const params = transactionData(resultA, resultB)
  // 新需求,增加弹框校验
  const newCheck = await checkApi(params)
  if (newCheck) {
    // 保存提交参数,我们实际需求不止这一个参数,有n多个参数后面要用到
    this.submitParams = params
    // 展示弹框
    this.showDialog = true
  }
  else{
    confirmSave()
  }
}
// 弹框确认函数,需要挪动大量代码
function confirmSave() {
  // 一堆其他处理,里面还有异步代码
  await saveApi(this.params)
}

上面的改造很明显能看出有2个问题

  1. 我需要增加一个确认函数,将原来函数获取参数后的逻辑挪到用户点击确认后再执行
  2. 我需要增加一个变量来保存参数,因为参数需要在确认函数内提交到后端

重点来了

但是我们的代码不是上面那么少的逻辑,我在挪动后面的逻辑时,不是1行2行代码就能搞定的,我可能需要拷贝几十上百行代码,内部用的变量也不是一个,因为很多异步,很多层层嵌套,这样改造风险太大。于是我想到了我们这次的主角Generator函数。

Generator用途

  1. 生成迭代器(Iterator)
    利用next()来迭代下一项
function* generateNumbers() {
  yield 1;
  yield 2;
  yield 3;
}

const iterator = generateNumbers();

console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
  1. 异步编程
function* asyncTask() {
  const result1 = yield asyncOperation1();
  const result2 = yield asyncOperation2(result1);
  return result2;
}

// 使用异步操作执行生成器函数
const iterator = asyncTask();
const promise1 = iterator.next().value;
promise1.then((value1) => {
  const promise2 = iterator.next(value1).value;
  promise2.then((value2) => {
    const finalResult = iterator.next(value2).value;
    console.log(finalResult); // 输出最终结果
  });
});

我这里场景跟上面的用途很像,首先我们是一个异步函数,提交完了需要在then里面继续执行逻辑。第二个我们的弹框也可以认为是一次异步逻辑,用户点击确认,则为异步成功,点击否,则为异步结束。于是我将saveData函数改为generator函数,在需要弹框的时候增加yield。用户确认之后继续执行next(true),用户点击否则next(false),让函数继续执行下去。这样好处很明显:

  1. 不用将参数后面的逻辑全部提取到新的函数,只需要弹出时增加yield来暂停执行
  2. 不用将保存用到的参数(params)挂载到this上(因为后面的逻辑需要用到这些参数)

下面是改造后的主要逻辑

// 页面点击保存执行的函数
aysnc function submitHandel() {
  await middleSaveData()
  // 提示保存成功,并且刷新列表
  this.showAlart('保存成功')
  this.queryList()
}
// 增加一个中间函数,来处理generatorFun函数的结果
function middleSaveData(){
  // 写一个promise,提供resolve和reject给主函数结束了调用
  return new Promise(resolve, reject) => {
      this.generatorFun = saveData(resolve, reject)
      // 开始执行主函数
      this.generatorFun.next()
  })
}
// 页面确认或取消的回调函数
function btnHandel(result){
  // 这里的result就是用户反馈的 是 或 否
  // 继续next,主函数会根据结果来最最终的逻辑resolve或者reject
  this.generatorFun.next(result)
}
// 增加*号,改造为generator函数,增加2个参数,resolve和reject
async function* saveData(resolve, reject){
  // 校验组件a表单数据
  const {checkA, resultA} = this.$refs.comA.checkData()
  if (!checkA) return Promise.reject()
  // 校验组件b表单数据
  const {checkB, resultB} = this.$refs.comB.checkData()
  if (!checkB) return Promise.reject()
  // 处理数据
  const params = transactionData(resultA, resultB)
  // 新需求,增加弹框校验
  const newCheck = await checkApi(params)
  if (newCheck) {
    // 保存提交参数,我们实际需求不止这一个参数,有n多个参数后面要用到
    this.submitParams = params
    // 展示弹框
    this.showDialog = true
    // 重点在这里,增加yiled暂停还是执行,等待用户操作后再执行next继续往后跑
    // 这里yield一次,相当于是一次很长时间的异步,由用户点击确认或取消来结束异步
    const result = yield
    // 用户拒绝
    if (!result) {
      reject()
    } 
  }
  // 一堆其他处理,里面还有异步代码
  await saveApi(params)
  // 提交完成
  resolve()
}

改造完毕,总体来看修改的点没有减少,但是最主要的是我们少了一个挪动大量代码(校验之后的部分)的逻辑。减少了bug的产生,因为后续逻辑是多个补丁版本加上的,并不是同一个开发来实现的,如果对需求不了解,很容易造成bug和大量的测试。上面的改造用到了generator函数增加yield来暂停逻辑,让用户反馈确认或取消后继续执行,来实现我们的需求,而主要的saveData几乎的逻辑几乎没有改变,只是增加了一个yield逻辑,对原来的逻辑没有破坏性。

最后

generator是es6(2015年)增加的,一直知道这个特性,奈何一直没有用武之地,这次虽然有点牵强,但总数用到实战中了,还是挺开心的。

很多时候我们学到一个知识点,然后用到项目中才算真正学以致用,但前提是你要知道有这个知识点,像这样的知识点公众号已经收录了500+。可以关注我的公众号:程序员每日三问。每天向你推送面试题,算法及干货,期待你的点赞和关注。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1003291.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

项目经理摆脱「计划无用」的秘诀!

项目经理面临的最大挑战是项目计划执行不到位,导致项目进度严重滞后。这种情况下,尽管他们手中握有项目计划,但实际上却形同虚设,几乎无法发挥应有的作用。 许多项目经理喜欢陈述一些既定的事实,强调一些难以克服的困…

虚幻动画系统概述

本文主要整理一下高层次的概述,方便后续查阅 1.动画流程 DCC产出动画文件 -> UE动画导入 -> 动画蓝图驱动(类似unity的动画状态机) ->动画后处理蓝图驱动(例如修型骨,骨骼矫正等后期处理) 2.动…

淘宝直通车质量分怎么提高?

1、直通车关键词新质量分是什么 (1)创意质量:就是获取流量的能力,和关键词在店铺中基本数据的展现 (2)买家体验:最终的成交能力,在有相关性的前提下,可以提升创意分和体…

【C++】C++多线程库的使用

C线程库的使用 一、线程库(thread)1、线程的id类2、线程对象的构造3、thread提供的其他成员函数4、this_thread命名空间5、线程函数的参数问题 二、互斥量库(mutex)1、mutex的种类2、lock_guard和unique_locklock_guardunique_loc…

marisa-trie——一个基于高效Trie树实现的快速高效字符串压缩存储、查询匹配工具实践

在前文中,讲到了因为实际项目的需要,调研了一下当前比较好用字符串查询匹配算法,感兴趣的话可以直接看下: 《pyahocorasick——基于AC自动机的python高效字符串匹配实践》 本文的主要目的同前文相同,这里主要是介绍一…

Linux 6.6 中的 SELinux 删除了 NSA 的引用

导读Security Enhanced Linux (SELinux) 二十年来一直是主线内核的一部分,它提供了一个实现访问控制安全策略的模块,现在广泛用于增强生产 Linux 服务器和其他系统的安全性。长期接触 Linux 的人可能不知道 SELinux 源自美国国家安全局 (NSA)。但是现在 …

centos 下 Makefile 独立模块编译ko

1、安装编译内核环境包 编译需要用到kernel 源码,centos 下需先安装 kernel-devel 包,要下与自己kernel 对应版本 yum install kernel-devel 2、首先从内核或自己写的模块,发到编译环境中 注:就像我自己拷贝一个 bcache 驱动的目…

pinduoduo(商品优惠券)API接口

为了进行电商平台 的API开发,首先我们需要做下面几件事情。 1)开发者注册一个账号 2)然后为每个pinduoduo应用注册一个应用程序键(App Key) 。 3)下载pinduoduo API的SDK并掌握基本的API基础知识和调用 4&#xff…

在Ubuntu系统中安装Docker

👨 作者简介:大家好,我是Taro,前端领域创作者 ✒️ 个人主页:唐璜Taro 🚀 支持我:点赞👍📝 评论 ⭐️收藏 文章目录 前言一、Ubuntu是什么?二、安装Docker1.…

2023-简单点-开启防火墙后,ping显示请求超时;windows共享盘挂在不上

情景描述 树莓派 挂载 windows共享盘 之前一直可以,突然有一天不行了 ping xxxx不通了 一查,或许是服务器被同事开了防火墙,默认关闭了ping的回显 操作: 开启ping回显cmd ping通了,但是挂载还是不行, 显示 dmesg命…

Mac电脑其他文件太占内存?如何进行删除

Mac老用户都知道在我们查看Mac内存时都会发现有一条“其他文件”占比非常高,它是Mac储存空间中的“其他”数据包含不可移除的移动资源,如,Siri 语音、字体、词典、钥匙串和 CloudKit 数据库、系统无法删除缓存的文件等。这些“其他文件”无用…

故障治理:如何进行故障复盘

故障复盘的重要性无需多说,每一次故障都是宝贵的学习机会,本人接手故障复盘工作已经半年有余,从一开始的手足无措,慢慢变得游刃有余。以下内容为本人从网上查阅学习多个专家经验,并结合工作经历总结而来,仅…

敏捷开发的几个要点

敏捷开发是一种以人为核心,迭代、增量式的软件开发方法。它强调团队成员的自我管理、面对变化时的快速适应能力,以及持续的沟通和协作。 以下是敏捷开发的几个要点: 敏捷宣言:敏捷开发遵循敏捷宣言,其中包括四个价值…

冠达管理:普通股是什么?

普通股是最常见的股票类型,由一家公司发行。买家以此实际上成为该公司的部分所有者,能够享有公司的股息和收益,一起还承当出资的危险。本文将从多个角度剖析普通股,其间包含普通股的定义、普通股的权益、普通股的优缺点、普通股的…

Python是否被高估了?

作为一门简洁易用、生态蓬勃且具有高泛用性的编程语言,Python一直以来都被不少人称作“编程语言中的瑞士军刀”。 尤其随着近来AI热潮席卷全球,Python在编程语言圈中的地位也随之水涨船高,甚至一度被视作AI专用语言或大数据专用语言。 然而…

华为交换机:MSTP的基础配置

现状分析 某公司的总部包含4个部门,为了增加网络的可靠性,需要所在的交换机上配置MSTP,确保网络不会出现环路问题,同时实现负载均衡。 网络设计 搭建网络拓扑,配置VLAN,Trunk,链路聚合。在4台…

腾讯云centos7.6安装部署备忘

1.Mysql 1.1 安装mysql wget http://dev.mysql.com/get/mysql-community-release-el7-5.noarch.rpm rpm -ivh mysql-community-release-el7-5.noarch.rpm yum install mysql-community-server 1.1.1 安装后重启 service mysqld restart 1.1.2 初次安装mysql,root账…

谁懂啊!自制的科普安全手册居然火了

自制的科普安全手册居然火了 谁懂啊! 嗨嗨嗨!小仙女们,有没有见过这样的可以翻页的电子安全手册呢?自己随手就能轻松制作手册,结果一晚浏览量这么多!这可真是让人又惊又喜啊!快来分享一下我的喜…

vscode 左侧文件夹不见了

1.选择view 2.选择open view 3.找到folder 4.左侧出现folder

安达发|离散型制造业更适合APS智能排产软件

在当今全球制造业竞争激烈的环境下,企业如何提高生产效率、降低成本、提升产品质量和满足客户需求,成为了制造业发展的关键。其中,智能化生产管理技术的应用,尤其是APS(Advanced Planning and Scheduling,智能计划与排程)智能排产…