面试官:听说过LazyMan吗,手写一个试试

简介: 我们都知道,JavaScript是单线程、基于异步的编程语言,执行代码时是非阻塞的。如果执行的是同步的代码,则按照顺序执行完毕;如果是异步的代码,则按照异步调用执行,并不会阻塞在某一个异步函数中。最近遇到了这样一个面试题……

我们都知道,JavaScript是单线程、基于异步的编程语言,执行代码时是非阻塞的。如果执行的是同步的代码,则按照顺序执行完毕;如果是异步的代码,则按照异步调用执行,并不会阻塞在某一个异步函数中。最近遇到了这样一个面试:LazyMan(懒汉):

const lazyMan = new LazyMan('张三')

lazyMan.eat('dinner').sleep(10).eat('super').sleepFirst(5).sleep(5);

其实主要就是要实现可阻塞代码执行的sleep函数,当然还有链式调用。接下来就看看怎么具体实现吧

思路

不管是写什么代码,在此之前,都应该有一个大致的设计思路。回到面试题,主要由以下特点:

  1. 需要支持链式调用,那么eatsleep等方法内需要返回当前对象this
  2. 需要支持sleep功能,那么函数就不能在直接调用时就触发。js是没有内置的sleep方法的,需要我们自己实现
  3. 初始化一个列表,将需要的函数都添加进去,然后依次执行
  4. 由于是链式调用,因此我们需要实现一个next的功能,在列表中的每一个函数都执行后能自动调用下一个任务来执行

思路看起来也不是很复杂,接下来就动手试试。

开始

简单版

先实现一个简单的LazyMan,为了便于理解,我们在最后一个链式调用中增加了done()方法,用来打印当前的任务列表:

class LazyMan {
    tasks = []; // 任务列表
    name = ''; // 懒汉
    constructor(name) {
        this.name = name;
        // 采用异步,确保所有的链式调用函数都添加到列表中后,自动执行第一个任务
        setTimeout(() => {
            this.next();
        }, 0)
    }
    // 执行下一个任务,每次执行完都会从任务列表中删除
    next() {
        const task = this.tasks.shift(); // 从头部删除,会直接影响原数组
        task && task();
    }
    // 阻塞,使得程序延后time执行
    sleep(time) {
        // 定义一个任务函数
        const task = () => {
            console.log(`sleep ${time}s 开始`)
            setTimeout(() => {
                console.log(`sleep ${time}s 结束`)
                this.next(); // 执行完当前函数后,继续执行下一个任务,直到任务执行完毕
            }, time * 1000); // 使用定时器来模拟实现阻塞功能
        }
        // 添加到任务列表中
        this.tasks.push(task);
        // 返回当前对象,保持链式调用
        return this;
    }
    eat(food) {
        // 定义一个任务函数
        const task = () => {
            console.log(`eat ${food}`)
            this.next(); // 执行完当前函数后,继续执行下一个任务,直到任务执行完毕
        }
        // 添加到任务列表中
        this.tasks.push(task);
        // 返回当前对象,保持链式调用
        return this;
    }
    done() {
        const task = () => {
            console.log(`当前待处理的任务:`)
            console.log(`${this.tasks}`)
            this.next();
        }
        this.tasks.unshift(task);
        return this;
    }
}
const lazyMan = new LazyMan('张三');
lazyMan.eat('dinner').sleep(10).eat('super').sleep(5).done();

实现效果:

image-20220618213825336.png

任务列表中其实就是一个个可执行函数

image-20220618213925712.png

实现效果基本上和LazyMan要求基本一致了。

看面试题中,除了sleep外,还有一个sleepFirst,这个其实就是说先执行sleep操作,即使这个函数调用并没有放在第一个。既然都已经实现了sleep了,要实现sleepFirst也并不复杂。

升级版

我们保持其他不变,仅来改动一下sleep函数:

class LazyMan {
    ...
    _sleep(time, isFirst) {
        const task = () => {
            console.log(`sleep ${time}s 开始`)
            setTimeout(() => {
                console.log(`sleep ${time}s 结束`)
                this.next(); // 执行完当前函数后,继续执行下一个任务,直到任务执行完毕
            }, time * 1000); // 使用定时器来模拟实现阻塞功能
        }
        if (isFirst) {
            this.tasks.unshift(task)
        } else {
            this.tasks.push(task)
        }
    }
    sleep(time) {
        this._sleep(time, false)
        return this
    }
    sleepFirst(time) {
        this._sleep(time, true)
        return this
    }
}
lazyMan.eat('dinner').sleep(10).eat('super').sleepFirst(5).sleep(5);

运行结果:

image-20220618220021843.png

懒汉会先执行sleepFirst

总结

LazyMan就简简单单实现了,如果不了解这个可能在一开始会有点蒙,其实看下来并不复杂。总结一下核心知识点:

  • 成员方法中返回this,可实现链式调用
  • 将调用的函数注册到列表中
  • 按照先进先出的原则,实现next方法,保证链式调用可以链式执行
  • 使用setTimeout的延后执行,来实现sleep
相关文章
|
机器学习/深度学习 人工智能 自然语言处理
未来趋势:人工智能在前端开发中的应用探索
随着人工智能技术的迅速发展,其在前端开发领域的应用也愈发广泛。本文将探讨人工智能如何改变前端开发的方式,以及未来人工智能在前端开发中的潜在应用和挑战。
430 13
|
存储 缓存 监控
如何写出一篇好的技术方案?
近期作者在写某个项目的技术方案时,来来回回修改了许多版,很是苦恼。于是,将自己之前写的和别人写的技术方案都翻出来看了几遍,产生了一些思考,分享给大家。
如何写出一篇好的技术方案?
聊天框(番外篇)—如何实现@功能的整体删除
上一篇文章中,我们已经初步实现了聊天输入框,但其@功能是不完善的,例如无法整体删除、无法获取除用户名以外的数据(假设用户名不是唯一的)。有问题就要想办法解决,在网上百度了一圈后,倒是有一些收获。本文就着重解决@的整体删除以及获取额外数据。
1737 0
聊天框(番外篇)—如何实现@功能的整体删除
|
Linux
linux下ffmpeg安装
<p style="border: 0px; font-family: Muli, sans-serif; font-size: 15px; margin-top: 0px; margin-bottom: 1.5em; outline: 0px; padding-top: 0px; padding-bottom: 0px; vertical-align: baseline; word-wrap
10363 57
|
JSON 前端开发 JavaScript
Vite 共享配置
本文介绍了 Vite 配置中的 `base`、`mode`、`plugins`、`resolve` 和 `css` 等关键配置项。`base` 用于设置公共基础路径,`mode` 指定环境模式,默认为 `development` 和 `production`。`plugins` 注册项目中使用的插件。`resolve` 包含别名配置、去重依赖项和模块入口字段等选项。`css` 配置 CSS 预处理器及其选项,如导入样式变量文件。
545 6
|
调度
DOS操作系统具有以下特点
【10月更文挑战第15天】DOS操作系统具有以下特点
763 2
|
JavaScript 前端开发 Java
面试官:假如有几十个请求,如何去控制并发?
面试官:假如有几十个请求,如何去控制并发?
|
达摩院 语音技术 异构计算
语音识别-免费开源的语音转文本软件Whisper的本地搭建详细教程,python版本是3.805,ffmpeg是专门处理音视频的,ffmpeg的下载链接,现在要求安装python和ffmpeg
语音识别-免费开源的语音转文本软件Whisper的本地搭建详细教程,python版本是3.805,ffmpeg是专门处理音视频的,ffmpeg的下载链接,现在要求安装python和ffmpeg
|
存储 监控 测试技术
Agent Workflow
【6月更文挑战第25天】
1384 3
|
运维 程序员 数据库
如何用TCC方案轻松实现分布式事务一致性
TCC(Try-Confirm-Cancel)是一种分布式事务解决方案,将事务拆分为尝试、确认和取消三步,确保在分布式系统中实现操作的原子性。它旨在处理分布式环境中的数据一致性问题,通过预检查和资源预留来降低失败风险。TCC方案具有高可靠性和灵活性,但也增加了系统复杂性并可能导致性能影响。它需要为每个服务实现Try、Confirm和Cancel接口,并在回滚时确保资源正确释放。虽然有挑战,TCC在复杂的分布式系统中仍被广泛应用。
1205 5