LazyMan 有几样写法,你知道么?
fi3ework opened this issue · comments
题目
实现一个 LazyMan,可以按照以下方式调用:
-
LazyMan("Hank")
输出:
Hi! This is Hank!
-
LazyMan("Hank").sleep(10).eat("dinner")
输出:
Hi! This is Hank!
等待10秒..
Wake up after 10
Eat dinner
-
LazyMan("Hank").eat("dinner").eat("supper")
输出:
Hi This is Hank!
Eat dinner
Eat supper
-
LazyMan("Hank").sleepFirst(5).eat("supper")
等待5秒
Wake up after 5
Hi This is Hank!
Eat supper
实现
1. callback
纯 callback 实现, 每个注册的事件的最后会调用对象队列中的下一个事件。
class LazyMan {
constructor(name) {
this.name = name
this.sayName = this.sayName.bind(this)
this.next = this.next.bind(this)
this.queue = [this.sayName]
setTimeout(this.next, 0)
}
callByOrder(queue) {
let sequence = Promise.resolve()
this.queue.forEach(item => {
sequence = sequence.then(item)
})
}
next(){
const currTask = this.queue.shift()
currTask && currTask()
}
sayName() {
console.log(`Hi! this is ${this.name}!`)
this.next()
}
holdOn(time) {
setTimeout(() => {
console.log(`Wake up after ${time} second`)
this.next()
}, time * 1000)
}
sleep(time) {
this.queue.push(this.holdOn(time))
return this
}
eat(meal) {
this.queue.push(() => {
console.log(`eat ${meal}`)
this.next()
})
return this
}
sleepFirst(time) {
this.queue.unshift(this.holdOn(time))
return this
}
}
2. Promise
手工在每次方法执行后通过 then 调整 Promise 链的序列,缺点是因为 sleepFirst 要强行插入 Promise 链的第一位,要单独抽象出一部分逻辑来前置它的 Promise。
class LazyMan {
constructor(name) {
this.name = name
this._preSleepTime = 0
this.sayName = this.sayName.bind(this)
this.p = Promise.resolve().then(() => {
if (this._preSleepTime > 0) {
return this.holdOn(this._preSleepTime)
}
}).then(this.sayName)
}
sayName() {
console.log(`Hi! this is ${this.name}!`)
}
holdOn(time) {
return new Promise(resolve => {
setTimeout(() => {
console.log(`Wake up after ${time} second`)
resolve()
}, time * 1000)
})
}
sleep(time) {
this.p = this.p.then(
() => this.holdOn(time)
)
return this
}
eat(meal) {
this.p = this.p.then(() => {
console.log(`eat ${meal}`)
})
return this
}
sleepFirst(time) {
this._preSleepTime = time
return this
}
}
3. Promise + 队列
在对象内部维护一个队列,让所有的事件都变成异步的,然后在内部通过 Promise.resolve.then() 来将队列的执行启动推迟到下一个 eventloop,这样做逻辑更清楚,所有事件都由队列来管理。
class LazyMan {
constructor(name) {
this.name = name
this.sayName = this.sayName.bind(this)
this.queue = [this.sayName]
Promise.resolve().then(() => this.callByOrder(this.queue))
}
callByOrder(queue) {
let sequence = Promise.resolve()
this.queue.forEach(item => {
sequence = sequence.then(item)
})
}
sayName() {
return new Promise((resolve) => {
console.log(`Hi! this is ${this.name}!`)
resolve()
})
}
holdOn(time) {
return () => new Promise(resolve => {
setTimeout(() => {
console.log(`Wake up after ${time} second`)
resolve()
}, time * 1000)
})
}
sleep(time) {
this.queue.push(this.holdOn(time))
return this
}
eat(meal) {
this.queue.push(() => {
console.log(`eat ${meal}`)
})
return this
}
sleepFirst(time) {
this.queue.unshift(this.holdOn(time))
return this
}
}
4. Promise + async
基本思路与第 2 种方法相同,不同的地方只在于使用了 async 来顺序执行队列。
class LazyMan {
constructor(name) {
this.name = name
this.sayName = this.sayName.bind(this)
this.queue = [this.sayName]
setTimeout(async () => {
for (let todo of this.queue) {
await todo()
}
}, 0)
}
callByOrder(queue) {
let sequence = Promise.resolve()
this.queue.forEach(item => {
sequence = sequence.then(item)
})
}
sayName() {
return new Promise((resolve) => {
console.log(`Hi! this is ${this.name}!`)
resolve()
})
}
holdOn(time) {
return () => new Promise(resolve => {
setTimeout(() => {
console.log(`Wake up after ${time} second`)
resolve()
}, time * 1000)
})
}
sleep(time) {
this.queue.push(this.holdOn(time))
return this
}
eat(meal) {
this.queue.push(() => {
console.log(`eat ${meal}`)
})
return this
}
sleepFirst(time) {
this.queue.unshift(this.holdOn(time))
return this
}
}
TODO
- yield 的实现版本
总结
整个过程其实还挺有意思的,多写几种方法挺练 Promise 和整个异步调用的思维的..
学习了
可以把任务看成三类:sleepFirst(微任务)、sayname(constructor任务)、eat&sleep(宏任务)
整个过程就是: 先串行执行微任务,再执行constructor任务,最后串行执行宏任务
class LazyMan {
constructor(name) {
this.name = name;
this.microTaskList = []; // 微任务
this.macroTaskList = []; // 宏任务
// Promise.resolve()使得先注册回调 再执行constructor
Promise.resolve()
.then(() => this.runInOrder(this.microTaskList))
.then(() => this.sayHello())
.then(() => this.runInOrder(this.macroTaskList));
}
sleepFirst(time) {
this.taskListAdd({type: 'micro', funcType: 'sleepFirst', params: {time}});
return this;
}
sleep(time) {
this.taskListAdd({type: 'macro', funcType: 'sleep', params: {time}});
return this;
}
eat(food) {
this.taskListAdd({type: 'macro', funcType: 'eat', params: {food}});
return this;
}
sayHello() {
console.log(`Hi This is ${this.name}`);
}
runInOrder(promiseArr) {
return promiseArr.reduce(
(prevPromise, nextPromise) => prevPromise.then(() => nextPromise()),
Promise.resolve()
);
}
taskListAdd({type, funcType, params}) {
let func = () => {};
switch (funcType) {
case 'sleep':
case 'sleepFirst':
func = () => new Promise(resolve => {
setTimeout(() => {
console.log(`Wake up after ${params.time}`);
resolve();
}, params.time * 1000);
});
break;
case 'eat':
func = () => new Promise(resolve => {
console.log(`Eat ${params.food}~`);
resolve();
});
break;
default:
break;
}
this[`${type}TaskList`].push(func);
}
}
这是我想到的版本,看到这个题目,我没有自己去写任务队列,而是使用了系统级别的宏任务和微任务
class LazyMan {
constructor(name) {
this.name = name;
setTimeout(() => {
console.log("Hi! This is " + name);
}, 0);
}
sleep(seconds) {
const delay = seconds * 1000;
const time = Date.now();
while (Date.now() - time < delay) {
// hu lu lu ~~
}
setTimeout(() => {
console.log("wake up after " + seconds);
}, 0);
return this;
}
eat(something) {
setTimeout(() => {
console.log("eat " + something);
}, 0);
return this;
}
sleepFirst(seconds) {
new Promise((resolve) => {
const delay = seconds * 1000;
const time = Date.now();
while (Date.now() - time < delay) {
// hu lu lu ~~
}
resolve();
}).then(() => {
console.log("wake up after " + seconds);
});
return this;
}
}
function lazyMan(name) {
return new LazyMan(name);
}
lazyMan("Hank").sleep(2).eat("dinner").sleepFirst(3);
function lazyMan (name) {
const tasks = []
const methods = {
say(name) {
tasks.push(() => console.log(`Hi! This is ${name}`))
return this
},
eat(food) {
tasks.push(() => console.log(`Eat ${food}`))
return this
},
sleepFirst(time) {
tasks.unshift(() => new Promise(resolve => setTimeout(resolve, time * 1000)))
return this
},
sleep(time) {
tasks.push(() => new Promise(resolve => setTimeout(resolve, time * 1000)))
return this;
}
}
setTimeout(function run() {
if(!tasks.length) return
Promise.resolve(tasks.shift()()).then(run)
}, 0)
methods.say(name)
return methods
}
// lazyMan('think2011').sleep(3).eat('supper')
// lazyMan('think2011').eat('dinner').eat('supper')
lazyMan('think2011').sleepFirst(3).eat('supper')
我来一个不使用 class 的