面试官:听说过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
相关文章
|
7月前
|
程序员 开发工具 Python
最全学Python有什么用?看完这些你肯定明白_学pysion的作用,2024年最新字节跳动面试严格吗
最全学Python有什么用?看完这些你肯定明白_学pysion的作用,2024年最新字节跳动面试严格吗
最全学Python有什么用?看完这些你肯定明白_学pysion的作用,2024年最新字节跳动面试严格吗
|
7月前
|
XML Java 数据格式
🚀今天,我们来详细的聊一聊SpringBoot自动配置原理,学了这么久,你学废了吗?
🚀今天,我们来详细的聊一聊SpringBoot自动配置原理,学了这么久,你学废了吗?
122 0
|
前端开发
【跳槽必备】2023常用手写面试题知识点总结(一)
【跳槽必备】2023常用手写面试题知识点总结(一)
82 0
|
JavaScript 前端开发 索引
【跳槽必备】2023常用手写面试题知识点总结(二)
【跳槽必备】2023常用手写面试题知识点总结(二)
85 0
|
前端开发 自动驾驶 算法
这个知识点99%的前端都没有听过,不信你进来看?
这个知识点99%的前端都没有听过,不信你进来看?
105 0
|
存储 资源调度 算法
不管卷不卷,面试还是得问问你G1原理!
所有的垃圾回收器的目的都是朝着减少STW的目的而前进,G1(Garbage First)回收器的出现颠覆了之前版本CMS、Parallel等垃圾回收器的分代收集方式,从2004年Sun发布第一篇关于G1的论文后,直到2012年JDK7发布更新版本,花了将近10年的时间G1才达到商用的程度,而到JDK9发布之后,G1成为了默认的垃圾回收器,CMS也变相地相当于被淘汰了。
不管卷不卷,面试还是得问问你G1原理!
|
存储 JavaScript 前端开发
面试官:你使用过IntersectionObserve方法吗?怎么用?
前言 不知道你有没有遇到过这样的问题:如何实现图片懒加载?如何判断元素进入了可视区?如何判断元素不在可视区内?等等...... 这些问题我相信绝大多数的前端小伙伴都遇到过,而且在项目中的遇到的频率还不低!我们就拿图片懒加载这种场景举例:当图片进入可视区后才进行加载。常见的做法就是通过监听 scroll 滚动事件,然后通过 getBoundingClientRect()实时获取元素的相对位置,从而判断元素是否出现在可视区内。 上面的方法需要频繁触发 scroll 事件,很容易造成卡顿或者页面性能问题。 处理这种问题,我们可以使用另一种方式:IntersectionObserve 方法。
515 0
面试官:你使用过IntersectionObserve方法吗?怎么用?
|
算法 Java 数据库
面试官:如何实现一个乐观锁(小白都能看得懂的代码)
java多线程中的锁分类多种多样,其中有一种主要的分类方式就是乐观和悲观进行划分的。这篇文章主要介绍如何自己手写一个乐观锁代码。不过文章为了保证完整性,会从基础开始介绍。
589 0
面试官:如何实现一个乐观锁(小白都能看得懂的代码)
|
存储 算法
看了齐姐这篇文章,再也不怕面试问树了(上)
在写完了所有线性数据结构之后,今天开启非线性数据结构系列。 我们今天先来看,什么是“树”
131 0
看了齐姐这篇文章,再也不怕面试问树了(上)

相关实验场景

更多
下一篇
DataWorks