首先请看网友通过setTimeout实现的:
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; } _LazyMan.prototype.sleepFirst = 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.unshift(fn); return this; } /* 封装 */ function LazyMan(name){ return new _LazyMan(name); } LazyMan('Hank').sleepFirst(5).sleep(10).eat('dinner')
分析:
用setTimeout模拟线程,有一个事件处理队列(this.tasks),然后每一个事件内部会调用事件调度函数(next),每一次的业务逻辑是通过定义的闭包函数fn,fn内部在处理完自身业务会执行next函数。
于是就想起处理这种含有异步并且有一定阻塞的业务,promise是非常合适的,实现思路是完全不同的。
首先得思考:
lazyman里边含有链式调用,那么每一个子任务 return this;这个程序支持任务优先顺序,那么就需要两个贯穿全场的Promise对象:第一,普通顺序promise;第二,插入顺序promise,同时插入顺序是阻塞普通顺序的,代码如下:
function _lazyman(name) { this.orderPromise = this.newPromise(); //定义顺序promise对象 this.insertPromise = this.newPromise(); //定义插入promise对象 this.order(function(resolve) { console.log(name); resolve(); }) } _lazyman.prototype = { /*实例化promise对象工厂 */ newPromise: function() { return new Promise(function(resolve, reject) { resolve(); }) }, order: function(fn) { var self = this; this.orderPromise = this.orderPromise.then(function() { return new Promise(function(resolve, reject) { //如果有insertPromise,阻塞orderPromise. self.fir ? self.insertPromise.then(function() { fn(resolve) }) : fn(resolve); }) }) }, insert: function(fn) { var self = this; this.fir = true; this.insertPromise = this.insertPromise.then(function() { return new Promise(function(resolve, reject) { fn(resolve) self.fir = false; }) }) }, firstTime: function(time) { this.insert(function(resolve) { setTimeout(function() { console.log('wait ' + time + ' s, other logic'); resolve(); }, time * 1000) })
return this; }, eat: function(something) { this.order(function(resolve) { console.log(something + '~~') resolve(); })
return this; }, sleep: function(time) { this.order(function(resolve) { setTimeout(function() { console.log('sleep ' + time + ' s'); resolve() }, time * 1000); })
return this; } } //接口封装。 function lazyman(name) { return new _lazyman(name); } //调用测试 lazyman('RoryWu').firstTime(1).sleep(2).firstTime(3).eat('dinner').eat('breakfast'); // 弹出: // wait 1 s, other logic // wait 3 s, other logic // RoryWu // sleep 2 s // dinner~~ // breakfast~~
注意每一个任务的业务逻辑都是通过回调函数的形式实现的,内部拥有两种API:
1 this.order(function(){ ... })
2 this.insert(function() {... })
这里的核心需要注意以下几点:
1 每一次任务的业务逻辑函数,就是这个回调函数传给对应的API,执行场地在它那。
2 每一个API的执行权利交给任务函数自身,是通过参数传递的方式,例如: fn(resolve),每一个任务函数在执行完自身业务逻辑后,通过resolve()改变Promise对象状态为Resolved(其他:rejected pending).
3 连个API的优先级实现,通过一个this.fir的状态值来判断是否含有插入promise对象,如果有就,就做一件事情改变fn发生的时间: insertPromise.then(function(){ fn(resolve) })
通过上边的实现,最后总结一下,在设计程序的时候,首先想想程序的大概构成,然后在开始写,这样大思路不会跑,进而很快就写出来了。