前端面试场景题 - LazyMan (HardMan/懒汉) - 事件队列的应用

题目要求

实现一个类 LazyMan,要求:

new LazyMan('煎饼狗子').drink('水') 输出

  • 我是煎饼狗子
    喝喝喝 水

new LazyMan('煎饼狗子').drink('水').sleep(3000).eat('憨包') 输出

  • 我是煎饼狗子
    喝喝喝 水
    // ......在这里等了3000ms
    睡了3000ms,起床
    吃吃吃 憨包

new LazyMan('煎饼狗子').drink('可乐').sleepFirst(1000) 输出

  • // ......在这里等了1000ms
    睡了1000ms,起床
    我是煎饼狗子
    喝喝喝 可乐

分析

这是一道很经典的面试题,这题不能用传统的想法来做,因为在使用new LazyMan('xxx').drink('x').sleep(3000).eat('x') 这样的点语法的时候,是同步任务,不可能等待睡觉几秒后才执行下一个。

可以用这样的思路,把 new LazyMan('xxx').drink('x').sleep(3000).eat('x') 看做注册任务, 放入一个数组中,等注册完了,再统一去执行,这样才方便我们控制

那么我们要怎么知道用户是否注册完任务了,再去统一执行?在这里我们可以利用事件队列 的特性,由于注册懒汉要做的事是同步任务(宏任务),我们只要用微任务即可,它自然就会等待所有宏任务执行完,自动去执行我们想要的操作。

代码实现

写法一:

ts 复制代码
type Task = (...params: any) => void | Promise<void>
class LazyMan {
    /**任务队列 */
    private queue: Task[] = []

    constructor(name: string) {
        this.add(() => console.log('我是 ' + name))
        setTimeout(() => { //利用宏任务的特性,确保同步任务执行完之后才来到这 (这里的同步任务指的是  lazyMan.drink('xxx').sleep(3000).eat('xx') 这样的事件注册)
            this.next()
        });
    }
    eat(food: string) {
        this.add(() => console.log('吃吃吃 ' + food))
        return this
    }
    sleep(time: number) {
        this.add(async () => {
            await new Promise<void>(r => setTimeout(() => {
                r()
                console.log(`睡了${time}ms,起床`);
            }, time))
        })
        return this
    }
    drink(thing: string) {
        this.add(() => console.log('喝喝喝 ' + thing))
        return this
    }
    sleepFirst(time: number) {
        this.addFirst(async () => {
            await new Promise<void>(r => setTimeout(() => {
                r()
                console.log(`睡了${time}ms,起床`);
            }, time))
        })
        return this
    }
    /**添加任务到末尾 */
    private add(task: Task) {
        this.queue.push(async () => {
            await task() //等待当前任务执行完后,接着执行下一个任务
            this.next()
        })
    }
    /**添加任务到开头 */
    private addFirst(task: Task) {
        this.queue.unshift(async () => {
            await task() //等待当前任务执行完后,接着执行下一个任务
            this.next()
        })
    }
    /**执行下一个任务 */
    private next() {
        this.queue.shift()?.()
    }
}

第二种写法: (区别在于添加任务的时候,不用手动调用next了,而是在runTask函数里去递归调用)

ts 复制代码
class LazyMan2 {
    private queue: Task[] = []
    constructor(name: string) {
        this.addTask(() => console.log('名字是: ' + name))
        setTimeout(() => {
            this.runTask()
        }, 0);
    }
    drink(name: string) {
        this.addTask(() => console.log('喝: ' + name))
        return this
    }
    eat(name: string) {
        this.addTask(() => console.log('吃: ' + name))
        return this
    }
    sleep(time: number) {
        this.addTask(() => {
            return new Promise(async (resolve, reject) => {
                setTimeout(() => {
                    console.log(`睡了 ${time} 秒`);
                    resolve()
                }, time * 1000)
            })
        })
        return this
    }
    private addTask(task: Task) {
        this.queue.push(task)
    }
    private async runTask() {
        const task = this.queue.shift()
        if (task) {
            await task()
            this.runTask()
        }
    }
}
相关推荐
王哈哈^_^40 分钟前
【数据集】【YOLO】【目标检测】交通事故识别数据集 8939 张,YOLO道路事故目标检测实战训练教程!
前端·人工智能·深度学习·yolo·目标检测·计算机视觉·pyqt
cs_dn_Jie1 小时前
钉钉 H5 微应用 手机端调试
前端·javascript·vue.js·vue·钉钉
好奇龙猫2 小时前
【学习AI-相关路程-mnist手写数字分类-win-硬件:windows-自我学习AI-实验步骤-全连接神经网络(BPnetwork)-操作流程(3) 】
人工智能·算法
开心工作室_kaic2 小时前
ssm068海鲜自助餐厅系统+vue(论文+源码)_kaic
前端·javascript·vue.js
有梦想的刺儿2 小时前
webWorker基本用法
前端·javascript·vue.js
sp_fyf_20242 小时前
计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-11-01
人工智能·深度学习·神经网络·算法·机器学习·语言模型·数据挖掘
cy玩具2 小时前
点击评论详情,跳到评论页面,携带对象参数写法:
前端
香菜大丸3 小时前
链表的归并排序
数据结构·算法·链表
jrrz08283 小时前
LeetCode 热题100(七)【链表】(1)
数据结构·c++·算法·leetcode·链表
oliveira-time3 小时前
golang学习2
算法