单例封装思想以及任务队列的hack方式
一、 单例封装思想
1.背景
我们经常在项目过程中会使用很多的工具函数,我们通常会用一个类进行封装,使用时,只需将工具文件进行import进入我们的代码中,实例化工具类即可使用封装的函数。
2.代码封装的过程
- 首先,假设我们现在需要进行两个数字的求和运算,并且多次在代码中调用,此时我们会在文件中生命一个函数add()。
function add (num1,num2) {
return num1 + num2;
}
- 其次,随着项目的开发和业务的增多,有个需求需要我们进行两个数字的求和运算,并且调用的频率很高,同样我们也可以封装一个函数出来。
function sub (num1,num2) {
return num1 - num2;
}
-
之后的开发过程中可能有许许多多的这种工具函数,所以我们最好封装起来,使用ES6语法,class的形式进行封装会更加直观。
class Utils { add(num1,num2) { return num1 + num2; } sub(num1,num2) { return num1 - num2; } } -
这样有个问题每次使用的时候都需要new这个utils类,当我们使用vue进行组件化开发的时候,就会产生一个问题,每个需要使用的.vue文件都需要new一下我们的工具包,这个时候我们可以直接挂载到main.js中的vue原型对象中去,使得改utils类的实例对象成为一个全局的对象,但是当我们的项目做大的时候,会有好几个工具类进行封装,这时候如果我们全部挂载到vue的原型对象上去容易出现命令冲突或者代码整洁度低,维护起来困难等问题。这时候我们可以使用单例模式的设计思想进行封装。就是在类里面实例化自己存在自己的静态成员(静态属性)中去,向外面暴露出一个getIntance()方法,从而拿到这个实例对象。接下来让我们看代码的实现部分:
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;
}
}
- 我们使用的时候就非常的方便,但是要主要我们业务的使用场景,需要使用实例化工具类的时候就用new关键字进行声明。平时的使用场景例如:
// HelloWorld.vue
import {Utils} from 'utils/utils'
export default {
methods:{
calculate(num1,num2) {
return Utils.getIntance().add(num1,num2) +
Utils.getInstance().sub(num1,num2);
}
}
}
二、任务队列的Hack方式
1.使用场景
最近,在公司的项目中有一个需求,是这样的,有一个轮询的任务队列需要进行场景的判断,我把它抽象出来。就是在while(true)条件下不断去执行循环体内的代码,每次执行的时候需要将代码的执行暂停住,这时候我们在内部判断是否已经到达跳出循环的条件,如果是,则我们就退出循环。
2.代码封装的过程
- 首先是,我们假设需要轮询的任务是一个简单自增的计算器。
function counter(maxNum) {
let count = 0;
while(true) {
counter++;
sleep() // 这是一个暂停while的向下执行的函数
if(count >= maxNum) return;
}
}
- 这样写的好处就是我们可以在sleep()函数中修改count的值,可以随时终止轮询的进行。在我们Javascript中,可以这样子写,使用异步函数配合await关键字达到暂停代码的向下执行的一个效果。
function sleep() {
new Promise((res,rej) => {
// 这个定时器在这里的效果是开启一个异步函数去执行它,从而配合await使用
setTimeout(() => {
// doSomething()...这里还可以进行操作,从而终止任务队列的执行
},0)
})
}
async function counter(maxNum) {
let count = 0;
while(true) {
counter++;
await sleep() // 这是一个暂停while的向下执行的函数
if(count >= maxNum) return;
}
}
- 思路大概是这个样子,我们还用类包裹起来,我们先做一个简单的Demo。
class Counter {
count;
flag;
constructor(){
this.count = 0;
this.flag = true;
}
sleep() {
return new Promise((res,rej) => {
// 这个定时器在这里的效果是开启一个异步函数去执行它,从而配合await使用
setTimeout(() => {
// doSomething()...这里还可以进行操作,从而终止任务队列的执行
if(this.count > 1000) {
this.flag = false;
}
},0)
})
}
async calculate(maxNum) {
while(this.flag && this.count < maxNum) {
this.count++;
await this.sleep() // 这是一个暂停while的向下执行的函数
}
// 这里我们判断一下是什么原因导致while循环结束的
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)
}
}
}
-
结果分析
测试结果如下图:
可以很直观的看到我们在sleep中终止了while循环,还可以暴露出去一个cancel方法用来控制while循环条件,从而达到跳出循环的目的。