链式调用和延迟执行

概述

工程化配置webpack中,在配置loader的时候,会看到能够写链式调用的写法,如下:

js 复制代码
config.module
  .rule('vue') // 创建一个名为 'vue' 的规则
    .test(/\.vue$/) // 规则的条件:匹配 .vue 文件
    .use('vue-loader') // 使用一个名为 'vue-loader' 的 loader
      .loader('vue-loader') // 指定 loader 的包名
      .end()
    .end()
  .rule('css') // 创建另一个名为 'css' 的规则
    .test(/\.css$/)
    .use('style-loader')
      .loader('style-loader')
      .end()
    .use('css-loader')
      .loader('css-loader');

这里实现类似链式调用的一个函数功能:有时候可能项目上需要插入多个任务,之间是链式执行的情况,通过链式调用来定义一个任务队列,这个队列可以混合立即执行的同步任务需要等待的异步任务(如定时器/异步任务) 。特别的是,它提供了一个 .waitFirst() 方法,可以将一个异步等待任务插入到整个队列的最前面 执行。最后调用 .execute() 来按顺序执行整个队列。

实现效果

任务:

js 复制代码
        // 任务
        createTask('A')
            .wait(2000).doSomething("第一件事")
            .wait(3000).doSomething("第二件事")
            .waitFirst(1000)
            .execute();
            
            
         

输出:

js 复制代码
Waited first for 1000 ms
Task A start
Waited for 2000 ms
Doing something in task 第一件事
Waited for 3000 ms
Doing something in task 第二件事

实现

js 复制代码
// 定义一个创建任务的函数,接受一个任务名称
function createTask(name) {
    // 初始化一个空数组,用于存储后续由各种方法添加的任务函数
    const taskList = []

    // 1. 首先,将一个"任务开始"的同步任务加入队列
    // 这是一个立即执行的同步任务,用于标记任务开始
    taskList.push(() => {
        console.log(`Task ${name} start`);
    })
    
    // 定义 wait 方法,它接受一个毫秒数 ms
    function wait(ms) {
        // 向任务队列尾部推送一个新的异步任务函数
        taskList.push(() => {
            // 这个任务返回一个 Promise,使其成为一个可等待的异步任务
            return new Promise((resolve) => {
                // 使用 setTimeout 实现等待
                setTimeout(() => {
                    console.log(`Waited for ${ms} ms`);
                    resolve(); // 等待完成后,解析 Promise,让队列继续执行下一个任务
                }, ms);
            });
        })
        // 返回 this(即 createTask 返回的对象),实现链式调用
        return this
    }

    // 定义 doSomething 方法,它接受一个消息 msg
    function doSomething(msg) {
        // 向任务队列尾部推送一个新的同步任务函数
        taskList.push(() => {
            console.log(`Doing something in task ${msg}`);
        })
        // 返回 this,实现链式调用
        return this
    }

    // 定义 waitFirst 方法,它接受一个毫秒数 ms
    // 这是关键方法,它与 wait 不同,将任务插入队列头部
    function waitFirst(ms) {
        // 使用 unshift 而不是 push,将任务插入到数组的最开头
        taskList.unshift(() => {
            return new Promise((resolve) => {
                setTimeout(() => {
                    console.log(`Waited first for ${ms} ms`);
                    resolve();
                }, ms);
            });
        })
        // 返回 this,实现链式调用
        return this
    }

    // 定义 execute 方法,用于开始按顺序执行任务队列
    function execute() {
        // 使用立即执行的异步函数表达式 (IIFE) 来执行异步遍历
        (async function () {
            // 使用 for...of 循环按顺序遍历任务队列
            for (const task of taskList) {
                // 使用 await 等待当前任务执行完成
                // 如果任务是同步的(如 doSomething),会立即执行
                // 如果任务是异步的并返回 Promise(如 wait, waitFirst),会等待其 resolve 后再继续
                await task()
            }
        })() // 立即调用这个异步函数

        // 仍然返回 this,虽然通常 execute 是链式调用的终点,但保持了API的一致性
        return this
    }

    // 返回一个对象,暴露出的方法可以供外部链式调用
    // 注意:这里返回的方法闭包引用了内部的 taskList 数组,这就是它们能操作同一个队列的原因
    return {
        wait,
        doSomething,
        waitFirst,
        execute
    }
}

// 使用示例:创建一个名为 'A' 的任务
createTask('A')
    // 链式调用:定义等待2秒后做第一件事
    .wait(2000).doSomething("第一件事")
    // 链式调用:定义再等待3秒后做第二件事
    .wait(3000).doSomething("第二件事")
    // 关键操作:将"首先等待1秒"这个任务插入到整个队列的最前面
    .waitFirst(1000)
    // 开始执行整个定义好的任务队列
    .execute();
相关推荐
崔庆才丨静觅8 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60619 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了9 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅9 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅9 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅10 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment10 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅10 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊10 小时前
jwt介绍
前端
爱敲代码的小鱼10 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax