开发日记(02) - js 异步任务队列

news/2024/7/10 0:37:53 标签: uni-app, javascript, vue.js, vue

开发日记(02) - js 异步任务队列

2021-01-31 20:40:22

0️⃣ 问题 ❓

算是之前项目遗留下来的一个问题。一直困扰着我。

还是关于 uni-app 以及 Vue 项目的网路请求,有这么一个需求,项目内有一个全局使用到数据,我们称为**“数据字典”**。需要在项目一打开就加载进来,存入到 Vuex,后续使用就不需要再请求网络了,使用的时候先判断 Vuex 内有没有数据,没有就去请求,有就用现成的。一般像这样的数据不需要经常更新。 有点类似于项目的全局配置,一打开就需要请求,然后在特定的页面需要用到。

因为是全局需要用到,所以我们在 App.vueonLaunch 应用生命周期(uni-app 的应用生命周期,与 Vuemounted 生命周期类似)进行一次请求

数据字典就是用来格式化类似性别订单状态的,比如后台返回一个订单列表,订单状态为 1,2,3,4,5…

前端不可能显示为纯数字,这个时候可以根据后台给到的解释进行判断显示。不过这种方法有一个缺点,就是每增加一种状态前端都得改代码。

数据字典就是一种比较好的解决方式,订单状态全部放在后端维护,前端使用数据字典配置好的状态说明进行格式化显示就很灵活了。

${app}/src/App.vue

javascript">import { getDictByKey } from '@/store/util'
export default {
  onLaunch() {
    console.log('App Launch')
    getDictByKey()
  },
}

在页面中这么用

${app}/src/pages/foo/foo.vue

javascript">import { getDictByKey } from '@/store/util'
export default {
  async onLoad() {
    // 请求类型信息
    const typeList = await getDictByKey('title_type', 'all')

    if (typeList && typeList.length) {
      this.typeList = typeList
    }
  },
}

这样用效果是可以实现的,但是我在 foo.vue 页面刷新页面的时候,就会触发两次相同的网络请求,拉了两遍请求数据字典的接口,因为 App.vue 有一次请求,foo.vue 也有一次网络请求。

这个接口数据量相对来说比较大,就会卡顿一下,拉取两次本来也是不正确的,虽然需求完成了,但是出于码农的强迫症和“职业道德”,这个问题不能蒙混过关,一定要解决它。

1️⃣ 解决方案

本地存一份数据

这是我们最开始用的解决方案,因为数据不经常更新,我们就把这个接口返回的 json 存为文件,然后在项目直接引入,只在 App.vue 进行一次请求更新。在页面内使用不请求。

一开始倒是没什么问题,项目经过迭代之后,数据字典模块也随之更新了,这样就造成了,假如我在 foo.vue 页面刷新数据,网络请求还没回来,本地没有的数据就显示空白。

异步任务队列

有看过关于任务队列的介绍,像 mqkafka,都是用来做消息队列的。 mysql 的事务隔离模式也有类似的。异步任务队列有点类似于 “串行化”,画张图大家感受下

flow.png

不管有多少个人问我要数据,我都把你们的请求存起来,我去拿数据,等我拿到了,我自己存起来,再一个个给你们。这样网络请求只发送一次,但是项目内同时可以有多个请求,类似的操作不仅仅在请求网络的时候能用到。

2️⃣ 代码实现异步任务队列

老规矩,直接上完整代码,代码不多,已经在项目内用上了,没有发现问题。拷贝需要修改成你自己的业务逻辑。

下面来慢慢分析代码,先说实话,点子是我自己想的,我实现不出来,就去网上找了蛮久,找到了一个看起来不错的优雅实现(其实就是代码比较少,改起来简单点 😁)

javascript">import store from '@/store/index'
import { handleApiRequestException } from '@/util/handle-error'

// 任务队列
const queue = []

/**
 * @name 通过字典的key值获取字典的value(添加任务)
 * @param {string} key 数据字典的 key 值
 * @param {*} value 用来标识请求所有还是单个值
 */
export function getDictByKey(key, value) {
  return new Promise((resolve) => {
    const task = { resolve, key, value }
    queue.push(task)
    if (queue.length === 1) {
      _next(task, true)
    }
  })
}

/**
 * @name 执行任务
 * @param {object} nextPromise 任务对象
 * @param {boolean} first 是否是第一个任务
 */
async function _next(nextPromise, first) {
  const { resolve, key, value } = nextPromise

  if (!store.state.dict.length) {
    try {
      await store.dispatch('getDictList')
      resolve(store.getters.filterDict(key, value))
    } catch (error) {
      handleApiRequestException(error)
      resolve(value === 'all' ? [] : '')
    }
  } else {
    resolve(store.getters.filterDict(key, value))
  }
  let task = queue.shift()
  if (first) {
    task = queue.shift()
  }
  task && _next(task)
}

我们从第一行开始看起

import 进来了两个东西,一个是 Vuex 实例,一个是错误处理。

queue 这个就是我们要的任务队列了。我们需要数据的请求,一个个往里面添加。后面执行完成了的任务会通过 shift 弹出去。

getDictByKey 就是请求,来看看页面怎么用的

javascript">import { getDictByKey } from '@/store/util'
// 获取时间单位类型
this.dateTypeList = await getDictByKey('time_type', 'all')

这里用到了 Promise 对象的一个特性,没有 resolve 就会一直阻塞。

_next 方法用来启动执行任务,传入一个任务和是否为第一个任务的标识,

  1. 取出任务

  2. 判断 Vuex 数据源是否已经有值

    如果有

    1. 直接 resolve 同步函数执行的结果

    如果没有

    1. 执行网络请求
    2. 请求成功存入 Vuex 数据仓库
    3. resolve 同步函数执行的结果
    4. 请求失败, resolve 空数据,同是进行错误处理(toast 提示)
  3. 拿到下一次任务

  4. 判断是不是第一次任务,如果是需要弹出,因为第一次任务已经执行过了,并且 resolve

  5. 判断任务是否存在,存在就继续使用 _next 任务执行函数执行第一步,没有任务就执行完毕了

3️⃣ 总结

是不是恍然大悟,还可以这样???😲😲😲,是的就是这么简单。当然如果你足够牛逼,可以加个异步任务出错重试,超时啥啥的,现在这样我们的项目够用了。


还有啊,如果你认真看了我的文章,解决了你的问题,我建议你关注下我,至少给我点个赞。你不要“不知好歹”,毕竟我还有很多问题的解决方案。

你们要是但凡有一个人给我提个问题,也不至于我王者荣耀周末“五连跪”!😤

📔 开发日记系列

只记录些平时开发觉得有用的东西,有问题请务必斧正,拜托了 🙏🙏🙏

  1. 开发日记(01) - uni-app 使用等宽字体对其数字显示
  2. 开发日记(02) - js 异步任务队列

关于我

SunSeekerX,前端开发、Nodejs 开发、小程序、uni-app开发、等等

喜欢探讨技术实现方案和细节,完美主义者,见不得bug

Github:https://github.com/SunSeekerX

个人博客:https://yoouu.cn/

个人在线笔记:https://doc.yoouu.cn/


http://www.niftyadmin.cn/n/1332105.html

相关文章

酸爽! Intellij IDEA 神器居然还藏着这些实用小技巧 !

点击上方 果汁简历 ,选择“置顶公众号”优质文章,第一时间送达Intellij IDEA真是越用越觉得它强大,它总是在我们写代码的时候,不时给我们来个小惊喜。出于对Intellij IDEA的喜爱,我决定写一个与其相关的专栏或者系列&a…

开发日记(03) - uni-app 打包为 app

uni-app 打包为 app 📌 前置条件 新手向,但需要有一些开发基础。 Git 官网下载:https://git-scm.com/downloads 使用以下命令验证是否可用 git version # 示例输出 # git version 2.32.0 (Apple Git-132)NodeJS nodejs 使用 lts 长期支持…

阿里 Nacos 惊爆安全漏洞,火速升级!

点击上方 果汁简历 ,选择“置顶公众号”优质文章,第一时间送达我发现nacos最新版本1.4.1对于User-Agent绕过安全漏洞的serverIdentity key-value修复机制,依然存在绕过问题,在nacos开启了serverIdentity的自定义key-value鉴权后&a…

开发日记(04) - iconfont 挂了,项目内的图标怎么办?

📌 0x1 问题 iconfont 应该是挂了有 1-2 两个月了,不管是因为什么原因,肯定会有一大堆的项目有一阵子图标出不来。 作为开发者以来就一直有个感觉,那就是在线服务都不可靠。项目中第三方服务越多,更多不可控的因素就…

Excel大批量数据的导入和导出,如何做优化?

点击上方 果汁简历 ,选择“置顶公众号”优质文章,第一时间送达概要 Java对Excel的操作一般都是用POI,但是数据量大的话可能会导致频繁的FGC或OOM,这篇文章跟大家说下如果避免踩POI的坑,以及分别对于xls和xlsx文件怎么优…

@Autowire和@Resource注解使用的正确姿势,别再用错的了!!

点击上方 果汁简历 ,选择“置顶公众号”优质文章,第一时间送达介绍今天使用Idea写代码的时候,看到之前的项目中显示有warning的提示,去看了下,是如下代码?Autowire private JdbcTemplate jdbcTemplate;提示的警告信息…

一个月薪 12000 的北京程序员的真实生活 !

来源:blog.csdn.net/qq_40433465/article/details/841967961. 关于学历2. 关于婚姻3. 关于性别4. 关于住房5. 关于吃6. 关于交通工具7. 关于爱好8. 关于性格9. 结尾“ 每个人都有自己的亲身经历,每个人都有一条生活道路。千万人,千万条&#…

一个注解搞定接口防刷!还有谁不会?

点击上方 果汁简历 ,选择“置顶公众号”优质文章,第一时间送达说明:使用了注解的方式进行对接口防刷的功能,非常高大上,本文章仅供参考。技术要点:springboot的基本知识,redis基本操作。首先是写…