前沿
前几天一个小伙伴面试,发现一个很有意思的面试题,之前自己也没有接触过,分享给大家。顺便在此记录一下,希望能够加深一下印象
题目
实现一个LazyMan,可以按照以下的方式调用
代码语言:javascript复制LazyMan('前端小本子')
// 输出:前端小本子
LazyMan('前端小本子').sleep(10).look('看完了')
// 输出:前端小本子
// 等待10秒
// 看完了
LazyMan('前端小本子').look('开始看').look('看完了')
// 输出:前端小本子
// 开始看
// 看完了
LazyMan('前端小本子').sleepFirst(5).look('看完了')
// 等待5秒
// 输出:前端小本子
// 看完了
分析
通过上面的题目大家不难看出,这是一种JavaScript的控制流的形式,这个问题的关键就是我们如何实现任务的顺序执行。
用过Express的应该都知道有个叫中间件的东西,中间件是什么,就跟我们题目中的等待以及看完了类似,当一个中间件完成之后,调用一下next然后执行下一个中间件,有了这个想法,那么我们就可以考虑实现了。
当然,还要跟大家啰嗦一点,就是这里还涉及到的知识点,就是事件轮训机制,队列等,所以当前我们要创建对应的任务队列,然后利用next进行任务的执行顺序,开搞
代码实现
代码语言:javascript复制// 定义一个LazyMan的构造函数
class _LazyMan {
constructor(name) {
this.tasks = [] //队列
const task = () => {
console.log(name)
this.next()
}
this.tasks.push(task)
setTimeout(() => {
this.next()
}, 0)
}
// 定义next函数,取到第一个函数,然后执行对应的方法,没有就不执行
next() {
const task = this.tasks.shift()
task && task()
}
// 定义look方法
look(something) {
const task = () => {
console.log(something)
this.next()
}
this.tasks.push(task)
// 返回this是为了链式调用
return this
}
// 定义sleep方法
sleep(time) {
this._sleepWrapper(time, false)
return this
}
// sleepFirst方法
sleepFirst(time) {
this._sleepWrapper(time, true)
return this
}
// 封装一下延时函数,第一个参数是延时时间,第二个为是否要推送到队列首位
_sleepWrapper(time, first) {
const task = () => {
setTimeout(() => {
console.log(`等待${time}秒`)
this.next()
}, time*1000)
}
if(first){
this.tasks.unshift(task)
}else{
this.tasks.push(task)
}
}
}
const LazyMan = (name) => {
return new _LazyMan(name)
}
结尾
顺便说一下,定时器里面去return this是不行的,定时器属于异步任务,通过事件循环调用回调函数,然而,同步任务会优先执行,也就是等到再执行异步任务的时候,变量已经成undefined了,原理就跟我们之前常碰到过的异步任务进行遍历最终结果一直是最后一个类似,如下,实在不明白就补一下js基础知识,奉上
代码语言:javascript复制function testAsync() {
for(var i = 0; i < 5; i ){
setTimeout(function() {
console.log(i)
},i*1000)
}
}
testAsync()
// 最终,每隔一秒打印出来的都是5
参考资料[1]
Reference
[1]
参考资料: https://zhuanlan.zhihu.com/p/22387417