稀土掘金技术社区 2024年12月15日
经典回溯,从一个老面试题(LazyMan)中学到的知识
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文深入探讨了如何实现一个名为LazyMan的JavaScript函数,该函数支持链式调用,并能按照特定顺序执行sleep、eat和sleepFirst等方法。文章首先回顾了题目要求,然后逐步展示了从最初的简单实现到最终使用任务队列控制执行顺序的完整过程。通过代码示例,详细解释了如何处理异步操作和改变调用顺序,最终实现了一个功能完善的LazyMan函数,展示了JavaScript中异步编程和任务队列的运用。

👋 **初始实现:** LazyMan函数接收一个参数name,并在控制台打印出“你好,我是${name}”。这是实现链式调用的基础,通过返回包含sleep、eat和sleepFirst方法的对象来实现。

⏳ **sleep和eat方法的实现:** sleep方法接收一个时间参数,模拟等待指定秒数后打印睡眠信息。eat方法则根据传入的参数打印不同的用餐信息。最初的实现使用了死循环进行等待,但后续被优化。

⏱️ **sleepFirst方法的挑战与解决方案:** sleepFirst方法的特殊之处在于它需要改变链式调用的执行顺序,将任务插入到队列的最前面。文章引入了任务队列的概念,使用unshift方法将sleepFirst的任务插入队列头部,并使用setTimeout模拟异步等待,使用next函数作为媒介,控制任务的执行顺序。

原创 陪我去看海 2024-12-15 09:00 重庆

点击关注公众号,“技术干货”及时达!

点击关注公众号,“技术干货” 及时达!

人们因能感知太多信息而感到心神不宁,或因产生过多欲望而痛苦不堪,又或因担忧能力不足而滋生焦虑,无论顺境或是逆境都不得安生。就像今天的我们,虽衣食无忧,却总是苦于无法摆脱手机的干扰,无法获取让人羡慕的技能,无法拥有想要的生活,等等。低层次的动物是没有这种烦恼的,它们的心灵只容纳环境中确实存在的、与它们切身相关的、靠直觉判断的信息——饥饿的狮子只注意能帮助它猎到羚羊的信息,吃饱的狮子的注意力则集中在温暖的阳光上...... ——《认知觉醒》

前言

“旧游如梦,重逢却是初见”,这句话很好的描述了再看见这个面试题的感觉,最开始知道这道题的时候,它只是道题,再看见时,它好像变成了很多知识点,这也是一种进步的吧。

题目

可能有些人不知道这道题目,所以有必要描述一下题目,这是一道经典题目

> LazyMan('Hank')
你好,我是 Hank
> LazyMan('Hank').sleep(10).eat('lunch')
你好,我是 Hank
(等10秒...)
我醒了,我刚睡了 10 秒
吃午餐
> LazyMan('Hank').eat('lunch').eat('supper')
你好,我是 Hank
吃午餐
吃晚餐
> LazyMan('Hank').sleepFirst(5).eat('supper')
(沉默5秒)
我醒了,我刚刚睡了 5 秒
你好,我是 Hank
吃晚餐

知道上述情况后,大致可以奠定LazyMan的基调

const LazyMan = (name) => {
 
 return {
 
  sleep(){},
  
  eat(){},
  
  sleepFirst(){}
  
  
 }
}
  
LazyMan("Hank")

清楚题目后,我们开始来实现吧,如果可以,我建议你可以自己先想一想!

实现 LazyMan('Hank')

从这里第一个打印来看,非常简单,只需要将这个参数值在控制台打印出即可

代码实现

const LazyMan = (name) => {
 console.log(`你好,我是${name}`)
 
 return {
 
  sleep(){},
  
  eat(){},
  
  sleepFirst(){}
  
 }
}
  
LazyMan("Hank")

这样就可以得到第一个打印结果:

第一步实现非常简单,对吧,接下来继续实现下一个

实现sleep(10)和eat('lunch')

首先看到LazyMan('Hank').sleep(10).eat('lunch')打印结果

你好,我是 Hank
(等10秒...)
我醒了,我刚睡了 10 秒
吃午餐

代码实现:

const LazyMan = (name) => {
 console.log(`你好,我是${name}`)
 
 const lazyMan = {
 
  sleep(second){
  
   let startTime = Date.now()
   
   while (Date.now() - startTime < 10000) { } // 空等 second 秒
   
   console.log(`我醒了,刚刚睡了${second}秒`)
   
   return lazyMan // 链式调用
  
  },
  
  eat(type){
  
   console.log("吃午餐")
  
  },
  
  sleepFirst(){}
 
 }
 
 return lazyMan
}
LazyMan("Hank").sleep(10).eat("lunch")

完工,这样我们也达到了这个调用的打印顺序要求了,查看结果:

接下来继续实现下一个

实现eat函数

我们先看到调用方式LazyMan('Hank').eat('lunch').eat('supper'),在看打印结果

你好,我是 Hank
吃午餐
吃晚餐

代码实现:

const LazyMan = (name) => {
 console.log(`你好,我是${name}`)
 
 const eatInfoMap = {
 
  'lunch'"吃午餐",
  
  'supper'"吃晚餐"
 
 }
 
 const lazyMan = {
 
  sleep(second){
  
   let startTime = Date.now()
   
   while (Date.now() - startTime < 10000) { } // 空等 second 秒
   
   console.log(`我醒了,刚刚睡了${second}秒`)
   
   return lazyMan // 链式调用
  
  },
  
  eat(type){
  
   console.log(eatInfoMap[type])
   
   return lazyMan
  
  },
  
  sleepFirst(){}
 
 }
 
 return lazyMan
}
LazyMan('Hank').eat('lunch').eat('supper')

这样似乎就满足了当前需求,查看打印结果:

实现了这个以后,我们继续实现

实现sleepFirst函数

看到调用方式LazyMan('Hank').sleepFirst(5).eat('supper'),再看打印结果

(沉默5秒)
我醒了,我刚刚睡了 5 秒
你好,我是 Hank
吃晚餐

这时候发现,如果继续采用之前的方式,好像永远也无法实现这个了,有点难受,这说明前面的想法都是错误的了再分析一下,sleepFirst可以改变链式调用的执行顺序,所以代码执行的顺序,需要我们进行控制,关于控制顺序的方式,刚好有执行队列呀,我们只需要把需要先执行的推送到执行队列最前方不就好了,然后考虑到有等待,可以让每一个执行队列的第一个任务执行完成后,再提醒后续执行,这样不就解决了嘛,那就试试吧!

删除之前代码,改变思路,使用一个队列来控制执行顺序

代码实现:

const LazyMan = (name) => {
 const eatInfoMap = {
 
  'lunch'"吃午餐",
  
  'supper'"吃晚餐"
 
 }
 
 
 const queue = [] // 任务队列
 
 const helloTask = () => {
 
  console.log(`你好,我是${name}`);
  
  next()
 
 }
 
 queue.push(helloTask) // 入任务队列,默认是第一个打印,出了特殊情况
 
   
 // 提醒需要执行后续函数,所以每个方法里执行了自己后,都需要向后传递,让后面执行
 const next = () => {
 
  const firstFn = queue.shift();
  
  firstFn?.()
 
 }
 
   
 
 const lazyMan = {
 
  sleep(s){
  
   const task = () => {
   
    setTimeout(() => {
    
     console.log(`我醒了,我刚睡了 ${s} 秒`)
     
     next()
   
    }, s * 1000)
   
   }
   
   queue.push(task)
   
   return lazyMan
  
  },
 
  eat(type){
  
   const task = () => {
   
    console.log(eatInfoMap[type])
    
    next()
   
   }
   
   queue.push(task)
   
   return lazyMan
  
  },
 
  sleepFirst(s){
  
   // 这个任务比较特殊,需要在最前方等待,所以直接推送到队列第一个
   const task = () => {
   
    setTimeout(() => {
    
     console.log(`我醒了,我刚刚睡了 ${s} 秒`)
     
     next()
    
    }, s * 1000)
   
   }
   
   queue.unshift(task)
   
   return lazyMan
  
  }
 
 }
   
 
 // 启动执行,但不能直接执行,如果直接执行的话,任务没有收集完,但是收集任务是同步的,所以要异步调用,所以就是手机完成后执行
 setTimeout(() => next())
 
 return lazyMan
}
LazyMan('Hank').sleepFirst(5).eat('supper')

查看结果:

至此,就完成了这道题目,我真的觉得相出这道题目的人是天才,哈哈哈,考点挺多的,如果之前没接触,还是很难写出来。

点击关注公众号,“技术干货” 及时达!

阅读原文

跳转微信打开

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

LazyMan 链式调用 JavaScript 异步编程 任务队列
相关文章