逛著博客看到一道很有意思的面試題毁腿,手癢癢就自己做了一下】撩看了標(biāo)準(zhǔn)答案之后發(fā)現(xiàn)自己真是‘思路精奇’??已烤,明明有更優(yōu)雅更簡單的實(shí)現(xiàn)方式,硬是用ugly的奇葩的方法做出來了妓羊。
題目要求是實(shí)現(xiàn)一個(gè)LazyMen的函數(shù)或類胯究,能用鏈?zhǔn)秸{(diào)用同步的eat方法和異步的sleep方法。難點(diǎn)在于類方法采用非回調(diào)的鏈?zhǔn)秸{(diào)用躁绸,但方法鏈中接在異步方法之后的函數(shù)必須等待異步完成后才能執(zhí)行裕循。
菜鳥的實(shí)現(xiàn):
(ps. 一開始的想法居然是在異步方法里強(qiáng)行阻塞丙猬,簡直為自己居然有這樣人神共憤天地不容的想法感到羞恥。)
實(shí)現(xiàn)是思路是费韭,既然無法回調(diào),就將異步調(diào)用的時(shí)間利用閉包存起來庭瑰,將同步方法延遲執(zhí)行星持。
const LazyMan = (function() {
let sleepFirst = 0,
sleep = 0;
const obj = {
sleep(time) {
sleep += time;
let curSleep = sleep;
setTimeout(() => {
setTimeout(() => {
sleep -= time;
console.log('Wake up after ' + time);
}, curSleep*1000)
})
return this;
},
eat(food) {
setTimeout(() => {
setTimeout(() => {
console.log('Eat ' + food + '~');
}, sleep*1000 + sleepFirst*1000)
}, 0)
return this;
},
sleepFirst(time) {
sleepFirst = time;
return this;
}
}
return function(name) {
setTimeout(() => {
if (sleepFirst) {
setTimeout(() => {
console.log('Wake up after ' + sleepFirst);
console.log('Hi! this is ' + name + '!');
sleepFirst = 0;
}, sleepFirst*1000)
} else {
console.log('Hi! this is ' + name + '!');
}
}, 0)
return obj;
}
}());
test:
這樣做的確完成了題目所要求的功能,但缺點(diǎn)也是顯而易見的:
- 異步事件時(shí)間未知的情況下弹灭,完全不可行督暂。而通常我們需要實(shí)現(xiàn)的異步操作(如ajax請求)的時(shí)間都是未知的,所以這方法相當(dāng)于沒有卵用穷吮;
- 因?yàn)槭莻萎惒綀?zhí)行逻翁,如果題目出現(xiàn)這樣的情況:
睡了吃了又睡又吃,就會出現(xiàn)錯(cuò)誤捡鱼。因?yàn)槲覀儗λ邥r(shí)間的處理是累加的八回,所有的吃的方法都會等待8s后才會被調(diào)用。
所以驾诈,正確的思路還是應(yīng)該想辦法運(yùn)用回調(diào)達(dá)到真正的異步順序執(zhí)行缠诅。
老司機(jī)的正確的姿勢:
class LazyMan {
constructor(name) {
this.queue = []
var hi = () => {
console.log(`Hi! This is ${name}!`)
this.next()
}
this.queue.push(hi)
setTimeout(() => {
this.next()
}, 0)
}
next() {
var fn = this.queue.shift()
fn && fn()
}
eat(food) {
var fn = () => {
console.log('eat ' + food)
this.next()
}
this.queue.push(fn)
return this
}
sleep(time) {
var fn = () => {
setTimeout(() => {
console.log('Wake up after '+ time + 's!')
this.next()
}, time*1000)
}
this.queue.push(fn)
return this
}
sleepFirst(time) {
var fn = () => {
setTimeout(() => {
console.log('Wake up after '+ time + 's!')
this.next()
}, time*1000)
}
this.queue.unshift(fn)
return this
}
}
**test: **
-----------------------------------\\完美//----------------------------------
- 首次鏈?zhǔn)秸{(diào)用時(shí),分別將方法push進(jìn)隊(duì)列中乍迄。
eat(food) {
var fn = () => {
console.log('eat ' + food)
this.next()
}
this.queue.push(fn)
return this
}
- 異步方法在完成時(shí)管引,通過調(diào)用this.next()去實(shí)現(xiàn)下個(gè)方法的調(diào)用,就實(shí)現(xiàn)了回調(diào)闯两。
sleep(time) {
var fn = () => {
setTimeout(() => {
console.log('Wake up after '+ time + 's!')
this.next()
}, time*1000)
}
this.queue.push(fn)
return this
}
這樣只要運(yùn)用一個(gè)通用的隊(duì)列和next()方法褥伴,就可以輕松實(shí)現(xiàn)異步回調(diào)啦!跪倒~