网上看过一道JavaScript试题:如何实现一个Lazyman?(本人第一次看到这个题目时心想什么是LazyMan?一脸懵逼~)
function _LazyMan(name){
this.name = name;
}
_LazyMan.prototype.sleep = function(time){
var self = this;
setTimeout(function(){
console.log(self.name+' Wake up after '+time)
return self?
}, time)
}
问题就出现在了 return这里,由于使用了时间控制setTimeout,return就出现了问题,这样任务就无法按照预想的顺序合理的继续下去。在Express有一个类似的东西叫中间件,这个中间件和我们这里的吃饭、睡觉等任务很类似,每一个中间件执行完成后会调用next()函数,这个函数用来调用下一个中间件,对于这个问题,正确的解决方案采用类似的思路来解决,首先创建一个任务队列,然后利用next()函数来控制任务的顺序执行:
function _LazyMan(name) {
this.tasks = [];
var self = this;
var fn =(function(n){
var name = n;
return function(){
console.log("Hi! This is " + name + "!");
self.next();
}
})(name);
this.tasks.push(fn);
setTimeout(function(){
self.next();
}, 0); // 在下一个事件循环启动任务
}
/* 事件调度函数 */
_LazyMan.prototype.next = function() {
var fn = this.tasks.shift();
fn && fn();
}
_LazyMan.prototype.eat = function(name) {
var self = this;
var fn =(function(name){
return function(){
console.log("Eat " + name + "~");
self.next()
}
})(name);
this.tasks.push(fn);
return this; // 实现链式调用
}
_LazyMan.prototype.sleep = function(time) {
var self = this;
var fn = (function(time){
return function() {
setTimeout(function(){
console.log("Wake up after " + time + "s!");
self.next();
}, time * 1000);
}
})(time);
this.tasks.push(fn);
return this;
}
/* 封装 */
function LazyMan(name){
return new _LazyMan(name);
}
分析该解决方案本质上是在任务启动时,首先将对象“人”和各个任务进行链接,并将各任务放入task数组里,当所有任务完成放入后,才从第一个任务开始进行顺序执行。明明对象构造函数执行的时候,等待0秒就应该开始执行的next函数,为什么会等到所有任务链接完成后才开始执行呢?这里起到关键作用的依然是setTimeout函数。这里的回调函数的执行是异步的,这种异步机制实际上依赖于浏览器的事件机制:Event Loop(事件循环)。
本文来自网易实践者社区,经作者鞠智宽授权发布。