Open qiuhongbingo opened 4 years ago
/** * 实现一个 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 /** * 分析 * 可以把 LazyMan 理解为一个构造函数,在调用时输出参数内容 * LazyMan 支持链式调用 * 链式调用过程提供了以下几个方法:sleepFirst、eat、sleep * 其中 eat 方法输出参数相关内容:Eat + 参数 * sleep 方法比较特殊,链式调用将暂停一定时间后继续执行,看到这里也许应该想到 setTimeout * sleepFirst 最为特殊,这个任务或者这个方法的 优先级最高 * 调用 sleepFirst 之后,链式调用将暂停一定时间后继续执行,尤其是最后一个 demo,sleepFirst 的输出优先级最高,调用后先等待 5 秒输出 Wake up after 5,再输出 Hi This is Hank! */ /** * 先从最简单的,我们可以封装一些基础方法,比如 log 输出、封装 setTimeout 等 * 因为 LazyMan 要实现一系列调用,且调用并不是顺序执行的,比如如果 sleepFirst 出现在调用链时,优先执行;同时任务并不是全部都同步执行的,因此我们应该实现一个任务队列,这个队列将调度执行各个任务 * 因此每次调用 LazyMan 或链式执行时,我们应该将相关调用方法加入到(push)任务队列中,储存起来,后续统一被调度 * 在写入任务队列时,如果当前的方法为 sleepFirst,那么需要将该方法放到队列的最头处,这应该是一个 unshift 方法 */ class LazyManGenerator { constructor(name) { this.taskArray = [] // 初始化时任务 const task = () => { console.log(`Hi! This is ${name}`) // 执行完初始化时任务后,继续执行下一个任务 this.next() } // 将初始化任务放入任务队列中 this.taskArray.push(task) setTimeout(() => { this.next() }, 0) } next() { // 取出下一个任务并执行 const task = this.taskArray.shift() task && task() } sleep(time) { this.sleepTask(time, false) // return this 保持链式调用 return this } sleepFirst(time) { this.sleepTask(time, true) return this } sleepTask(time, prior) { const task = () => { setTimeout(() => { console.log(`Wake up after ${time}`) this.next() }, time * 1000) } if (prior) { this.taskArray.unshift(task) } else { this.taskArray.push(task) } } eat(name) { const task = () => { console.log(`Eat ${name}`) this.next() } this.taskArray.push(task) return this } } function LazyMan(name) { return new LazyManGenerator(name) } /** * 总结 * LazyMan 方法返回一个 LazyManGenerator 构造函数的实例 * 在 LazyManGenerator constructor 当中,我们维护了 taskArray 用来存储任务,同时将初始化任务放到 taskArray 当中 * 还是在 LazyManGenerator constructor 中,将任务的逐个执行即 next 调用放在 setTimeout 中,这样就能够保证在开始执行任务时,taskArray 数组已经填满了任务 * 我们来看看 next 方法,取出 taskArray 数组中的首项,进行执行 * eat 方法将 eat task 放到 taskArray 数组中,注意 eat task 方法需要调用 this.next() 显式调用“下一个任务”;同时返回 this,完成链式调用 * sleep 和 sleepFirst 都调用了 sleepTask,不同在于第二个参数:sleepTask 第二个参数表示是否优先执行,如果 prior 为 true,则使用 unshift 将任务插到 taskArray 开头 */