共计 3273 个字符,预计需要花费 9 分钟才能阅读完成。
开发日记(02) – js 异步工作队列
2021-01-31 20:40:22
0️⃣ 问题 ❓
算是之前我的项目遗留下来的一个问题。始终困扰着我。
还是对于 uni-app
以及 Vue
我的项目的网路申请,有这么一个需要,我的项目内有一个全局应用到数据,咱们称为“数据字典”。须要在我的项目一关上就加载进来,存入到 Vuex
,后续应用就不须要再申请网络了,应用的时候先判断 Vuex
内有没有数据,没有就去申请,有就用现成的。个别像这样的数据不须要常常更新。有点相似于我的项目的全局配置,一关上就须要申请,而后在特定的页面须要用到。
因为是全局须要用到,所以咱们在 App.vue
的 onLaunch
利用生命周期(uni-app
的利用生命周期,与 Vue
的 mounted
生命周期相似)进行一次申请
数据字典就是用来格式化相似 性别 , 订单状态 的,比方后盾返回一个订单列表,订单状态为 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
页面刷新数据,网络申请还没回来,本地没有的数据就显示空白。
异步工作队列
有看过对于工作队列的介绍,像 mq
、kafka
,都是用来做音讯队列的。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
办法用来启动执行工作,传入一个工作和是否为第一个工作的标识,
- 取出工作
-
判断
Vuex
数据源是否曾经有值如果有
- 间接
resolve
同步函数执行的后果
如果没有
- 执行网络申请
- 申请胜利存入
Vuex
数据仓库 resolve
同步函数执行的后果- 申请失败,
resolve
空数据,同是进行错误处理(toast 提醒)
- 间接
- 拿到下一次工作
- 判断是不是第一次工作,如果是须要弹出,因为第一次工作曾经执行过了,并且
resolve
了 - 判断工作是否存在,存在就持续应用
_next
工作执行函数执行第一步,没有工作就执行结束了
3️⃣ 总结
是不是豁然开朗,还能够这样???????????????,是的就是这么简略。当然如果你足够牛逼,能够加个异步工作出错重试,超时啥啥的,当初这样咱们的我的项目够用了。
还有啊,如果你认真看了我的文章,解决了你的问题,我倡议你关注下我,至多给我点个赞。你不要“不知好歹”,毕竟我还有很多问题的解决方案。
你们要是凡是有一个人给我提个问题,也不至于我王者光荣周末“五连跪”!????
???? 开发日记系列
只记录些平时开发感觉有用的货色,有问题请务必斧正,托付了 ????????????
- 开发日记(01) – uni-app 应用等宽字体对其数字显示
- 开发日记(02) – js 异步工作队列
对于我
SunSeekerX,前端开发、Nodejs 开发、小程序、uni-app
开发、等等
喜爱探讨技术实现计划和细节,完满主义者,见不得bug
。
Github:https://github.com/SunSeekerX
集体博客:https://yoouu.cn/
集体在线笔记:https://doc.yoouu.cn/