关于javascript:开发日记02-js-异步任务队列

41次阅读

共计 3273 个字符,预计需要花费 9 分钟才能阅读完成。

开发日记(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 的事务隔离模式也有相似的。异步工作队列有点相似于“串行化”,画张图大家感触下

不论有多少集体问我要数据,我都把你们的申请存起来,我去拿数据,等我拿到了,我本人存起来,再一个个给你们。这样网络申请只发送一次,然而我的项目内同时能够有多个申请,相似的操作不仅仅在申请网络的时候能用到。

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/

正文完
 0