前几天看有人问这个问题, 自己研究了一下.
后面的 promise 版, 绝对原创, 估计有识货的.
具体问题是:
[quote] 实现一个 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[/quote]
此问题在我看来就是一个异步调用问题.
要调用的函数用数组保存起来, 然后逐个异步调用即可.
因此我的思路和网上的解法有点不一样. 不管异步任务还是同步任务, 我都把它变成异步的.
当然, 写法的形式会有几种:
1.1 闭包写法, 最开始的思路 (运行效果时, 有啥问题请拷贝到本地)
html 代码
<script>
function LazyMan(name) {
var tasks = [];
var next = function() {
var fn = tasks.shift();
fn && setTimeout(fn);
};
var print = function(msg) {
document.write(msg + "<br>");
};
var man = {
hi: function(name) {
tasks.push(function() {
print("Hi! This is" + name + "!");
next();
});
return this;
},
eat: function(thing) {
tasks.push(function() {
print("eat" + thing + "~");
next();
});
return this;
},
sleep: function(time) {
tasks.push(function() {
setTimeout(function() {
print("Wake up after" + time);
next();
}, time * 1000);
});
return this;
},
sleepFirst: function(time) {
tasks.unshift(function() {
setTimeout(function() {
print("Wake up after" + time);
next();
}, time * 1000);
});
return this;
}
};
man.hi(name);
setTimeout(next);
return man;
};
//LazyMan("Hank")
//LazyMan("Hank").sleep(5).eat("dinner")
//LazyMan("Hank").eat("dinner").eat("supper")
LazyMan("Hank").sleepFirst(3).eat("supper")
</script>
1.2 改成面向对象的风格. 注意不需要 new 的写法, 和 bind 的使用.
html 代码
<script>
function LazyMan(name) {
if (!(this instanceof LazyMan)) return new LazyMan(name);
this.tasks = [];
this.hi(name);
setTimeout(this.next.bind(this));
}
LazyMan.prototype = {
print: function(msg) {
document.write(msg + "<br>");
},
next: function() {
var fn = this.tasks.shift();
fn && setTimeout(fn.bind(this));
},
hi: function(name) {
this.tasks.push(function() {
this.print("Hi! This is" + name + "!");
this.next();
});
return this;
},
eat: function(thing) {
this.tasks.push(function() {
this.print("eat" + thing + "~");
this.next();
});
return this;
},
sleep: function(time) {
this.tasks.push(function() {
setTimeout(function() {
this.print("Wake up after" + time);
this.next();
}.bind(this), time * 1000);
});
return this;
},
sleepFirst: function(time) {
this.tasks.unshift(function() {
setTimeout(function() {
this.print("Wake up after" + time);
this.next();
}.bind(this), time * 1000);
});
return this;
}
};
//LazyMan("Hank")
//LazyMan("Hank").sleep(5).eat("dinner")
//LazyMan("Hank").eat("dinner").eat("supper")
LazyMan("Hank").sleepFirst(3).eat("supper")
</script>
1.3 改成 ES6 的风格
html 代码
<script>
class LazyManClass {
constructor(name) {
this.tasks = [];
this.hi(name);
setTimeout(this.next.bind(this));
}
print(msg) {
document.write(`${msg}<br>`);
}
next() {
let fn = this.tasks.shift();
fn && setTimeout(fn);
}
hi(name) {
this.tasks.push(() => {
this.print(`Hi! This is ${name}!`);
this.next();
});
return this;
}
eat(thing) {
this.tasks.push(() => {
this.print(`eat ${thing}~`);
this.next();
});
return this;
}
sleep(time) {
this.tasks.push(() => {
setTimeout(() => {
this.print(`Wake up after ${time}`);
this.next();
}, time * 1000);
});
return this;
}
sleepFirst(time) {
this.tasks.unshift(() => {
setTimeout(() => {
this.print(`Wake up after ${time}`);
this.next();
}, time * 1000);
});
return this;
}
}
function LazyMan(name) {
return new LazyManClass(name);
}
//LazyMan("Hank")
//LazyMan("Hank").sleep(5).eat("dinner")
//LazyMan("Hank").eat("dinner").eat("supper")
LazyMan("Hank").sleepFirst(3).eat("supper")
</script>
用 promise 来实现的话, 数组也是必须有的. 关键是如何构成 promise 链, 这是异步的典型问题.
2.promise chain 版 (可以封装成类的形式, 这里省略)
html 代码
<script>
function LazyMan(name) {
var print = function(msg) {
document.write(msg + "<br>");
};
var tasks = [];
var hi = function(name) {
return new Promise(function(resolve) {
setTimeout(function() {
resolve();
});
}).then(function() {
print("Hi! This is" + name + "!");
});
};
var eat = function(thing) {
return new Promise(function(resolve) {
setTimeout(function() {
resolve();
});
}).then(function() {
print("eat" + thing + "~");
});
};
var sleep = function(time) {
return new Promise(function(resolve) {
setTimeout(function() {
resolve();
}, time * 1000);
}).then(function() {
print("Wake up after" + time);
});
};
var man = {
hi: function(name) {
tasks.push(hi.bind(null, name));
return this;
},
eat: function(thing) {
tasks.push(eat.bind(null, thing));
return this;
},
sleep: function(time) {
tasks.push(sleep.bind(null, time));
return this;
},
sleepFirst: function(time) {
tasks.unshift(sleep.bind(null, time));
return this;
}
};
man.hi(name);
setTimeout(function() {
tasks.reduce(function(promise, task) {
return promise.then(task);
}, Promise.resolve());
});
return man;
};
//LazyMan("Hank")
//LazyMan("Hank").sleep(5).eat("dinner")
//LazyMan("Hank").eat("dinner").eat("supper")
LazyMan("Hank").sleepFirst(3).eat("supper");
</script>
后记
这两种实现方式, 应该算可以了. 至于用其他实现方式的话, 个人感觉有点高射炮打蚊子.
本文完.
------ 补充一种写法, 更简练些.
html 代码
<script>
function LazyMan(name) {
var tasks = [];
var next = function() {
var fn = tasks.shift();
fn && fn();
};
var print = function(msg) {
document.write(msg + "<br>");
};
var man = {
hi: function(name) {
tasks.push(setTimeout.bind(null, function() {
print("Hi! This is" + name + "!");
next();
}));
return this;
},
eat: function(thing) {
tasks.push(setTimeout.bind(null, function() {
print("eat" + thing + "~");
next();
}));
return this;
},
sleep: function(time) {
tasks.push(setTimeout.bind(null, function() {
print("Wake up after" + time);
next();
}, time * 1000));
return this;
},
sleepFirst: function(time) {
tasks.unshift(setTimeout.bind(null, function() {
print("Wake up after" + time);
next();
}, time * 1000));
return this;
}
};
man.hi(name);
setTimeout(next);
return man;
};
//LazyMan("Hank")
//LazyMan("Hank").sleep(5).eat("dinner")
//LazyMan("Hank").eat("dinner").eat("supper")
LazyMan("Hank").sleepFirst(3).eat("supper")
</script>