The idea of singleton encapsulation and the hack of task queue

The idea of singleton encapsulation

1. The background

We often use a lot of utility functions in the project process, we usually use a class to encapsulate, just import the utility file into our code, the instance class can use the encapsulated function.

2. Process of code encapsulation

  1. First, suppose we now need to sum two numbers and call them multiple times in code, we will add() a function in our file.
function add (num1,num2) {
  return num1 + num2;
}
Copy the code
  1. Secondly, with the development of the project and the increase of business, there is a demand for us to carry out the sum operation of two numbers, and the call frequency is very high, and we can also encapsulate a function out.
function sub (num1,num2) {
  return num1 - num2;
}
Copy the code
  1. There may be many more of these utility functions later in the development process, so it is better to encapsulate them, using ES6 syntax, in class form.

    class Utils { add(num1,num2) { return num1 + num2; } sub(num1,num2) { return num1 - num2; }}Copy the code
  2. The problem is that we need the utils class new every time we use it. When we componentalize using vue, we have a problem. Every.vue file we need to use needs to be new in our toolkit. It makes the instance object of the utils class become a global object. However, when our project gets bigger, there will be several tool classes to encapsulate. At this time, if we mount all the prototype object of vue, it will be easy to have command conflict or low code cleanliness, difficult to maintain and other problems. At this time we can use the design idea of singleton pattern encapsulation. The instance is to instantiate its own static member (static attribute) inside the class, and expose a getIntance() method to get the instance object. Let’s look at the implementation of the code:

class Utils {
  static _instance;
  constructor(){};
  static getInstance() {
    if(!this._instance) {
      this._instance = new Utils();
    }
    return this._instance;
  }
  add(num1,num2) {
    return num1 + num2;
  }
  
  sub(num1,num2) {
  return num1 - num2;
  }
}
Copy the code
  1. When we use it, it is very convenient, but to use the main scenarios of our business, we need to use the new keyword to declare when we need to use the instance tool class. Typical usage scenarios are as follows:
// HelloWorld.vue import {Utils} from 'utils/utils' export default { methods:{ calculate(num1,num2) { return Utils.getIntance().add(num1,num2) + Utils.getInstance().sub(num1,num2); }}}Copy the code

Second, the task queue Hack

1. Application scenarios

Recently, in a company project, there was a requirement that looked like this, that there was a polling queue of tasks that needed to do scenario judgment, and I abstracted it out. It is to execute the code inside the loop continuously under the condition of while(true). Each time we need to pause the execution of the code, at this time we determine internally whether the condition to break out of the loop has been reached, if so, we exit the loop.

2. Process of code encapsulation

  1. First, let’s assume that the task requiring polling is a simple self-increasing calculator.
function counter(maxNum) { let count = 0; while(true) { counter++; If (count >= maxNum) return; if(count >= maxNum) return; }}Copy the code
  1. The advantage of this is that we can change the value of count in sleep() and stop polling at any time. In Javascript, we can write something like this, using an asynchronous function with the await keyword to pause the code as it moves down.
Function sleep() {new Promise((res,rej) => { To use setTimeout(() => {// doSomething()... },0)})} async function counter(maxNum) {let count = 0; while(true) { counter++; Await sleep() if(count >= maxNum) return; }}Copy the code
  1. So the idea is this, we’re going to wrap it around a class, and we’re going to do a very simple Demo.
class Counter { count; flag; constructor(){ this.count = 0; this.flag = true; } sleep() {return new Promise((res,rej) => {// The effect of this timer is to start an asynchronous function to execute it, To use setTimeout(() => {// doSomething()... If (this.count > 1000) {this.flag = false; } },0) }) } async calculate(maxNum) { while(this.flag && this.count < maxNum) { this.count++; Await this.sleep() {// we are awaiting this.sleep() {// we are awaiting this.sleep() {if (this.count >= maxNum) { console.log('calculated: count, count: ', this.count, ' flag: ', this.flag) } if (! this.flag) { console.log('calculated: flag, count: ', this.count, ' flag: ', this.flag) } } }Copy the code
  1. Results analysis

    The test results are shown below:

It can be seen intuitively that we terminate the while loop in sleep and expose a cancel method to control the condition of the while loop, so as to break out of the loop.