Development Diary (02) – JS asynchronous task queue

The 2021-01-31 20:40:22

0 ️ ❓ ⃣ problem

Kind of a hangover from the previous project. It’s been bothering me.

Again regarding the uni-app and Vue project network requests, there is a requirement that there is a globally used data within the project, which we call ** “data dictionary” **. The project needs to be loaded in as soon as it is opened and stored in Vuex, so there is no need to request network for subsequent use. When using Vuex, first judge whether there is data in Vuex, request it if there is no data, and use ready-made data if there is. Normally data like this doesn’t need to be updated very often. It is similar to the global configuration of a project, which is requested as soon as it is opened and then used on a specific page.

Because it is a global need, so we in the App. Vue onLaunch application lifecycle (uni – App application lifecycle, similar to the mounted lifecycle vue) to make a request

The data dictionary is used to format things like gender, order status, for example, return a list of orders in the background, order status 1,2,3,4,5…

The front end can not be displayed as pure numbers, this time you can judge the display according to the explanation given by the background. The downside of this approach is that you have to change the code each time you add a state to the front end.

The data dictionary is a better solution. The order status is maintained in the back end, and the front end uses the data dictionary to format and display the configured status.

${app}/src/App.vue

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

Use this in pages

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

import { getDictByKey } from '@/store/util'
export default {
  async onLoad() {
    // Request type information
    const typeList = await getDictByKey('title_type'.'all')

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

This is possible, but when I refresh foo.vue, I trigger the same network request twice, pulling the data dictionary interface twice, because app. vue has one request and foo.vue has one network request.

This interface has a relatively large amount of data, so it will be delayed, and it would be incorrect to pull it twice. Although the requirement is completed, this problem cannot be muddied through due to the obsessive compulsive behavior and “professional ethics” of the coder, so it must be solved.

1️ solution

Save a copy of data locally

This was our initial solution. Since data doesn’t update very often, we store the JSON returned by this interface as a file and import it directly into the project, requesting only one update in app.vue. Use no requests within the page.

At first there was nothing wrong. As the project iterated, the data dictionary module was updated, so that if I refreshed the data on foo.vue, the data that was not locally available would be blank before the network request came back.

Asynchronous task queue

I have read about task queues, such as MQ, Kafka, are used to do message queues. Mysql has a similar transaction isolation mode. Asynchronous task queues are a little bit like serialization, so let me draw a picture for you

No matter how many people ask me for data, I’ll save your requests, I’ll get the data, and when I get it, I’ll save it myself, and I’ll give it to you one by one. The network request is sent only once, but there can be multiple requests in the project at the same time, and similar operations can be used not only for network requests.

2️ code to realize asynchronous task queue

Same rule, directly on the complete code, code is not much, has been used in the project, no problem found. Copy needs to be modified to your own business logic.

Next to slowly analyze the code, first to tell the truth, the idea is I want to, I can not achieve it, went to the Internet for a long time, found a looks good elegant implementation (in fact, the code is less, change up simple 😁)

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

// Task queue
const queue = []

/ * * *@name Get the dictionary's value (add task) * from the dictionary's key value@param {string} Key Indicates the key value * of the data dictionary@param {*} Value is used to indicate whether a single value */ is requested
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 Perform tasks@param {object} NextPromise task object *@param {boolean} First is the first task */
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)
}
Copy the code

Let’s start with the first line

Import comes in two things, a Vuex instance and error handling.

Queue this is the task queue we want. We need requests for data to be added one by one. The tasks that are completed by the subsequent execution will pop out with Shift.

GetDictByKey is a request to see how the page works

import { getDictByKey } from '@/store/util'
// Get the time unit type
this.dateTypeList = await getDictByKey('time_type'.'all')
Copy the code

This uses a feature of the Promise object that blocks until resolve is available.

The _next method is used to start executing a task, passing in a task and an identifier for whether it is the first task,

  1. Take out the task

  2. Determine whether the Vuex data source already has a value

    If you have

    1. directlyresolveThe result of the synchronization function execution

    If there is no

    1. Executing network requests
    2. Request successful depositVuexThe data warehouse
    3. resolveThe result of the synchronization function execution
    4. Request failed,resolveEmpty data, same error handling (TOAST prompt)
  3. Get the next mission

  4. Determine if it is the first task, if it needs to be ejected, because the first task has already been executed, and resolve is resolved

  5. If the task exists, continue to use the _next task execution function to perform the first step. If the task does not exist, the execution is complete

3 ️ ⃣ summary

Is suddenly enlightened, can also be so?? 😲😲😲, yes it’s that simple. Of course, if you’re cool enough, you can add an asynchronous task, retry error, timeout, whatever, and that’s enough for our project right now.


By the way, if you read my article carefully and solve your problem, I suggest you follow me and at least give me a like. Don’t be “ungrateful”. After all, I still have solutions to many problems.

If one of you had asked me a question, it wouldn’t have made me kneel in a row at King of Glory Weekend! 😤

📔 Development diary series

Only record some of the usual development of useful things, please be sure to correct questions, please 🙏🙏🙏

  1. Development Diary (01) – UNI-app uses the same width font for its digital display
  2. Development Diary (02) – JS asynchronous task queue

About me

SunSeekerX, front-end development, Nodejs development, applets, Uni-app development, etc

Like to discuss technical implementation schemes and details, perfectionist, not afraid of bugs.

Github:github.com/SunSeekerX

Personal blog: yoouu.cn/

Personal online notes: doc.yoouu.cn/