【JS基础】一些个人积累的原生JS编码设计思想,和大家一起开拓下思维

文章目录

  • 前言
  • 对象配置
  • 链式调用
  • 队列调用
  • 并发执行
  • 未完待续

前言

以下都是我个人遇到的前端JS原生编码设计上的一些案例记录,希望能帮助新手开拓写代码的思想,并且能够结合自己的想法应用在实际的项目中,写出更加易读,拓展,维护的代码。

在其中会有一些案例展示,并不是说某个写法只能用于该案例上,要学会举一反三。

还有一点就是不要死记这些东西(我自己也记不住,叫我重写都未必能写出),留有个印象就好,等到某天你发现某个场景可以使用到下面的写法时,再对应的拿取用。


对象配置

就是一个总函数,可以通过传入的对象配置项,开启函数内部的一些特定模块的处理,例子如下:

// 总函数
function fn(target, config = {}) {
    // A模块处理默认开启
    if (config.handleA === true || config.handleA === undefined) {
        handleA()
    }
    // B模块手动开启
    if (config?.handleA === true) {
        handleB()
    }
}

// handleA处理模块
function handleA(){

}

// handleB处理模块
function handleB(){

}

// ...

举个使用在项目上的例子,例如我封装axios的时候,如果是一些简单的项目,就只需要做一层封装即可,然后在写接口请求方法的时候只需要:

import request from "@/utils/request";

/**
 * 登请求
 */
export const login = (data) => {
  return request({
    url: "/sys/login",
    method: "POST",
    data,
    needLoading: true, // 是否有请求动画,默认给true
    handleErrer: false, // 是否要手动处理错误信息,默认会自动报出接口错误信息
    // ...
  });
};

然后在axios的请求和响应拦截器中实现对应的功能即可。


链式调用

这是我个人认为最好理解和记忆的编写方法:

class _Print {
    // 初始化
    constructor() {
        this.queue = [this.init] // 执行栈
        this.next()
    }

    // 初始化钩子
    init() {
        console.log('初始化钩子')
        // 这里要开启下一轮事件循环再执行栈中的任务,保证链式调用的任务已推入
        setTimeout(() => {
            this.next();
        }, 0)
    }

    // 同步执行
    print(msg) {
        let fn = function () {
            console.log(msg);
            this.next()
        }
        this.queue.push(fn)
        return this
    }

    // 延迟
    delay(time) {
        let fn = function () {
            setTimeout(() => {
                this.next()
            }, time)
        }

        this.queue.push(fn)
        return this
    }

    // 弹出栈任务并执行
    next() {
        let fn = this.queue.shift() // 这里重新定义了函数,不再是指向实例了
        fn && fn.call(this)
    }
}

new _Print().print('1').delay(3000).delay(3000).print('2')

问题来了,这种链式调用能用在什么场景下呢?暂时没想到哈哈。


队列调用

就是把要经过的任务都推入到任务队列里,然后挨个执行,例子如下:

function p() {
    let promise = Promise.resolve()

    function fn1(result) { // 功能封装1
        console.log('fn1');
        return Promise.resolve('fn1')
    }

    function fn2(result) { // 功能封装2
        console.log('fn2');
        return Promise.resolve('fn2')
    }

    let arr = [fn1, fn2]

    while (arr.length) {
        promise = promise.then(arr.shift())
    }

    return promise
}

p('1') // fn1 fn2 轮流执行

可以用在对axios进行更深入的封装,可以参考我这篇文章:【场景方案】如何去设计并二次封装一个好用的axios,给你提供一个另类写法,另加一些思考


并发执行

并发执行任务的时候,我们要做好每次的并发量。一般这种并发场景都是异步请求,所以必然涉及到Promise,这里也就拿Promise去写示例:

// 模拟100个异步请求
const arr = [];
for (let i = 0; i < 100; i++) {
    arr.push(() => new Promise((resolve) => {
        setTimeout(() => {
            console.log('done', i);
            resolve();
        }, 100 * i);
    }));
};

const parallelRun = () => {
    const runingTask = new Map(); // 记录正在发送的异步请求(闭包存储)
    const inqueue = (totalTask, max) => { // 异步请求队列,每组请求的最大数量
        // 当正在请求的任务数量小于每组请求的最大数量,并且还有任务未发起时,就推入请求
        while (runingTask.size < max && totalTask.length) {
            const newTask = totalTask.shift(); // 弹出新任务
            const tempName = totalTask.length; // 以长度命名?
            runingTask.set(tempName, newTask);
            newTask().finally(() => {
                runingTask.delete(tempName);
                inqueue(totalTask, max); // 每次一个任务完成后就继续塞入新任务
            });
        }
    }
    return inqueue;
};

parallelRun()(arr, 6);

有人会问为啥不直接用all方法呢?因为只要期中一个任务失败了,整个队列都没用了。详细可以看【es6入门】好好捋一捋Promise与Async的异步写法,细节满满


未完待续

你可能感兴趣的:(#,JS基础,javascript,开发语言,ecmascript)