第 56 题:要求设计 LazyMan 类,实现以下功能
LazyMan('Tony'); // Hi I am Tony LazyMan('Tony').sleep(10).eat('lunch'); // Hi I am Tony // 等待了10秒... // I am eating lunch LazyMan('Tony').eat('lunch').sleep(10).eat('dinner'); // Hi I am Tony // I am eating lunch // 等待了10秒... // I am eating diner LazyMan('Tony').eat('lunch').eat('dinner').sleepFirst(5).sleep(10).eat('junk food'); // Hi I am Tony // 等待了5秒... // I am eating lunch // I am eating dinner // 等待了10秒... // I am eating junk food
Answer
class LazyManClass { constructor(name) { this.taskList = []; this.name = name; console.log(`Hi I am ${this.name}`); setTimeout(() => { this.next(); }, 0); } eat (name) { var that = this; var fn = (function (n) { return function () { console.log(`I am eating ${n}`) that.next(); } })(name); this.taskList.push(fn); return this; } sleepFirst (time) { var that = this; var fn = (function (t) { return function () { setTimeout(() => { console.log(`等待了${t}秒...`) that.next(); }, t * 1000); } })(time); this.taskList.unshift(fn); return this; } sleep (time) { var that = this var fn = (function (t) { return function () { setTimeout(() => { console.log(`等待了${t}秒...`) that.next(); }, t * 1000); } })(time); this.taskList.push(fn); return this; } next () { var fn = this.taskList.shift(); fn && fn(); } } function LazyMan(name) { return new LazyManClass(name); } LazyMan('Tony').eat('lunch').eat('dinner').sleepFirst(5).sleep(4).eat('junk food');
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(56)
有大佬能解答下next()调用的时机吗,这个运行流程我有点看不懂=。=
我不是大佬,但我理解的next()应该是在LazyMan('Toney')的时候异步执行的,因为异步,所以它还没干活的时候,链式调用早结束了,taskList也把要干的活按照规则存好了,随后它开始干活,由于taskList中每个活函数执行完后又主动调用了一次next(),所以能一直把taskList中的活干完
如果真是这样那就好理解了,谢谢解答!!
在constructor中, next()方法执行在 setTimeout 中,setTimeout 是一个task,当 js 运行到 setTimeout 的时候,会把他放到 task 任务队列中,等到所有的同步的任务执行完后,就会执行setTimeout 中的this.next()方法 , 这个问题的关键问题是 js 的事件队列问题 。
你可以参考这个链接详细了解 https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/7?from=groupmessage&isappinstalled=0
@GuidingStarKe
首先谢谢解答和分享!
用了async,不需callback, 下面是两种方法放一起了。
第一种方法注释掉了,调用任务eat或者sleep或者sleepFirst时,把任务的data存放队列里,在init的方法里异步调用async executeTasks, 任务存放是同步的,先于异步的方法executeTasks执行完,在executeTasks方法里依次让任务data出列,然后执行相应的任务。
第二种是,使用next方法,每个任务最后都会调用一次next方法,把每个任务都压入队列,next的一次调用也是异步的,晚于同步的任务入列执行,next方法里让当前的任务入列,并执行。
有没有大佬解答一下,这里传立即执行函数的返回值(函数)和直接传红字部分的函数,有什么区别吗
立即执行函数的作用是在内部函数还没有执行的时候就已经为内部函数绑定好了对应参数的值,如果不用立即函数的话也可以用bind方法
这样一来无论你在什么地方执行fn都不需要传参数了,直接
fn()
不用参数也能达到和普通fn的fn(name )
效果一样了不用类的实现,js里基本用类实现的用对象也可以实现。
下面是实现
sleep怎么能用setTimeout来实现呢,这样只会限制sleep使用次数,多次出现sleep,就会有bug吧。幸好例子是只给了一个sleep,只能说过于特殊了,还可能是故意为之吧。
终于翻到的解析
订阅发布、任务机制(异步串行)
1、回调嵌套实现
2、promise 迭代实现
测试
可以这样用,task 箭头函数不接受参数,直接用外部 eat 方法的参数
for await of
+async iterable
方式:递归方式:
应该是闭包 保存变量 吧
类似于消息队列一样,排队取出任务执行,
sleepFirst
就像是优先级更高的任务构造函数中加入开始执行任务队列的语句会导致延迟添加的任务无法执行,例如:
以下是我的解决方法,将setTimeout封装进“添加任务”的函数中以实现延迟执行
用防抖来解:
function lazyMan(name) {
class LazyMan {
constructor(name) {
this.name = name;
this._queues = [];
console.log(
Hi I am ${name}
);setTimeout(() => {
this.next();
}, 0);
}
sleep(t) {
const fn = (() => {
return async () => {
console.log(
等待了${t}s
);this.next();
};
})(t);
this._queues.push(fn);
return this;
}
eat(food) {
const fn = (() => {
return async () => {
console.log(
I am eating${food}
);this.next();
};
})(food);
this._queues.push(fn);
return this;
}
sleepFirst(t) {
const fn = (() => {
return async () => {
console.log(
等待了${t}s
);this.next();
};
})(t);
this._queues.unshift(fn);
return this;
}
next() {
const fn = this._queues.shift();
fn && fn();
}
}
}
lazyMan("Tony")
.eat("lunch")
.eat("dinner")
.sleepFirst(5)
.sleep(10)
.eat("junk food");
写个链表实现
Proxy 版本