软件教程 2025年08月6日
0 收藏 0 点赞 698 浏览 3649 个字
摘要 :

文章目录 一、基础功能实现思路 (一)示例代码准备 (二)Queue类的初始化 (三)work函数的功能实现 二、进阶功能拓展 (一)新增push方法 (二)pushQueue方法的……




  • 一、基础功能实现思路
    • (一)示例代码准备
    • (二)Queue类的初始化
    • (三)work函数的功能实现
  • 二、进阶功能拓展
    • (一)新增push方法
    • (二)pushQueue方法的定义
    • (三)修改work函数

    在前端开发的面试环节中,经常会遇到这样一类题目:要求实现控制并发队列,给定异步任务和并发数量作为参数,完成特定的并发控制功能。不少小伙伴面对这类问题时会感到头疼,今天就带大家一步步从0到1实现Promise并发控制,用通俗易懂的方式讲解,保证大家都能看明白。

    一、基础功能实现思路

    (一)示例代码准备

    首先,我们通过一个简单的delay函数来模拟异步任务。这个函数接收两个参数,一个是要返回的文本text,另一个是延迟的时间time。它返回一个Promise,在设定的延迟时间后,resolve返回对应的文本。

    function delay(text,time){
        return new Promise((resolve,reject) => {
            setTimeout(() => {
                resolve(text)
            },time)
        })
    }
    

    接着,定义一些具体的异步任务p1p5,它们分别返回不同的文本,并且延迟时间也不一样。

    const p1 = () => delay(\'1\',5000)
    const p2 = () => delay(\'2\',2000)
    const p3 = () => delay(\'3\',3000)
    const p4 = () => delay(\'4\',2000)
    const p5 = () => delay(\'5\',3000)
    const p = [p1,p2,p3,p4,p5]
    

    (二)Queue类的初始化

    我们要创建一个Queue类来实现并发控制。在初始化这个类的时候,需要设置异步任务数组和并发数量limit。在constructor构造函数里,除了把传入的参数赋值给类的属性外,还要从异步任务数组的开头取出limit个任务,添加到一个任务队列queue里,并且默认直接开始执行这些异步任务。

    class Queue {
        constructor(initArr, limit) {
            this.queue = [];
            this.limit = limit;
            this.lists = initArr;
    
            for (let i = 0; i < this.limit; i++) {
                this.queue.push(this.lists.shift())
            }
            this.work();
        }
        
        work() {
            // 后续会定义这个函数的具体功能
        }
    
        limit = 0;
        queue = [];
        lists = [];
    }
    

    (三)work函数的功能实现

    work函数的作用是执行当前任务队列queue里的所有任务。在执行过程中,每当有一个异步任务完成,就从剩余的异步任务数组lists里取出第一个任务,添加到任务队列queue中。

    pop() {
        if (this.lists.length > 0) {
            this.queue.push(this.lists.shift());
        }
    }
    
    work() {
        while(this.queue.length > 0) {
            const requestFunc = this.queue.shift();
            const request = requestFunc();
            // 使用Promise.resolve确保request不是promise时,代码也能正常执行
            Promise.resolve(request).finally(res => {
                this.pop();
                this.work();
            })
        }
    }
    

    这样,基础版本的Promise并发控制就实现啦,完整代码如下:

    class Queue {
        constructor(initArr, limit) {
            this.queue = [];
            this.limit = limit;
            this.lists = initArr;
    
            for (let i = 0; i < this.limit; i++) {
                this.queue.push(this.lists.shift())
            }
            this.work();
        }
    
        pop() {
            if (this.lists.length > 0) {
                this.queue.push(this.lists.shift());
            }
        }
    
        work() {
            while(this.queue.length > 0) {
                const requestFunc = this.queue.shift();
                const request = requestFunc();
                Promise.resolve(request).finally(res => {
                    this.pop();
                    this.work();
                })
            }
        }
    
        limit = 0;
        queue = [];
        lists = [];
    }
    

    二、进阶功能拓展

    有时候,我们希望在使用过程中还能往任务队列里添加新的任务。比如,在已经创建了Queue实例q后,还能通过q.push(() => delay(\'6\', 3000))这样的方式添加新任务。这就需要对之前的代码进行升级。

    (一)新增push方法

    我们新增一个外部可以调用的push方法。这个方法可以接收一个任务或者一个任务数组。如果传入的是数组,就把数组里的任务都添加到lists数组里;如果传入的是单个任务,就直接把这个任务添加到lists数组里。添加完任务后,调用pushQueue方法来处理是否把新任务添加到任务队列queue里。

    push(item) {
        if (item instanceof Array) {
            this.lists.push(...item);
        } else {
            this.lists.push(item)
        }
    
        this.pushQueue()
    }
    

    (二)pushQueue方法的定义

    pushQueue方法的作用是检查当前是否有条件把lists数组里的任务添加到任务队列queue中。如果lists数组里有任务,并且当前正在执行的异步任务数量小于并发限制limit,就把lists里的任务添加到queue里。这里新增了一个running变量,用来记录当前正在执行的异步任务数量。

    pushQueue() {
        if (this.lists.length > 0) {
            for (let i = 0; i < this.limit - this.running; i++) {
                const item = this.lists.shift()
                if (item) this.queue.push(item);
            }
        }
    }
    

    (三)修改work函数

    因为新增了running变量,所以在work函数里,当开始执行一个异步任务时,要把running加1;当异步任务完成时,要把running减1。同时,任务完成后不再直接调用pop方法,而是调用pushQueue方法来处理任务队列。

    work() {
        while(this.queue.length > 0) {
            const requestFunc = this.queue.shift();
            const request = requestFunc();
            // 开始执行异步任务,running加1
            this.running += 1;
            Promise.resolve(request).finally(res => {
                // 异步任务完成,running减1
                this.running -= 1;
                // 通过pushQueue处理任务队列
                this.pushQueue();
                this.work();
            })
        }
    }
    

    升级后的完整代码如下:

    class Queue {
        constructor(initArr, limit) {
            this.queue = [];
            this.limit = limit;
            this.lists = initArr;
    
            this.pushQueue();
    
            this.work();
        }
    
        pushQueue() {
            if (this.lists.length > 0) {
                for (let i = 0; i < this.limit - this.running; i++) {
                    const item = this.lists.shift()
                    if (item)
                        this.queue.push(item);
                }
            }
        }
    
        push(item) {
            if (item instanceof Array) {
                this.lists.push(...item);
            } else {
                this.lists.push(item)
            }
    
            this.pushQueue()
        }
    
        work() {
            while(this.queue.length > 0) {
                const requestFunc = this.queue.shift();
                const request = requestFunc();
                this.running += 1;
                Promise.resolve(request).finally(res => {
                    this.running -= 1;
                    this.pushQueue();
                    this.work();
                })
            }
        }
    
        limit = 0;
        queue = [];
        lists = [];
        running = 0
    }
    

    通过以上一步步的实现,无论是基础的Promise并发控制,还是进阶的动态添加任务功能,都能顺利完成啦。希望这篇文章能帮助大家更好地理解和掌握Promise并发控制的实现原理,在面试和实际开发中都能轻松应对相关问题。

微信扫一扫

支付宝扫一扫

版权: 转载请注明出处:https://www.zuozi.net/6802.html

管理员

相关推荐
2025-08-06

文章目录 一、Promise基础回顾 二、Promise 与 axios 结合使用场景及方法 (一)直接返回 axios …

269
2025-08-06

文章目录 一、模块初始化时的内部机制 二、常见导出写法的差异分析 (一)写法一:module.exports…

107
2025-08-06

文章目录 一、ResizeObserver详解 (一)ResizeObserver是什么 (二)ResizeObserver的基本用法 …

683
2025-08-06

文章目录 一、前期准备工作 (一)下载相关文件 (二)安装必要工具 二、处理扣子空间生成的文件…

338
2025-08-06

文章目录 一、官方文档 二、自动解包的数据类型 ref对象:无需.value即可访问 reactive对象:保持…

371
2025-08-06

文章目录 一、Hooks的工作原理 二、在if语句中使用Hook会出什么岔子? 三、React官方的Hook使用规…

843
发表评论
暂无评论

还没有评论呢,快来抢沙发~

助力内容变现

将您的收入提升到一个新的水平

点击联系客服

在线时间:08:00-23:00

客服QQ

122325244

客服电话

400-888-8888

客服邮箱

122325244@qq.com

扫描二维码

关注微信客服号