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

开发日记(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

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

在页面中这么用

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

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️⃣ 代码实现异步任务队列

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

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

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 就是请求,来看看页面怎么用的

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/

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,651评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,468评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,931评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,218评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,234评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,198评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,084评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,926评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,341评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,563评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,731评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,430评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,036评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,676评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,829评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,743评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,629评论 2 354

推荐阅读更多精彩内容