在掘金看到的文章,流程控制同步和异步任务的顺序执行,收益匪浅,工作中能用到。
1、实现以下效果
实现一个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 以此类推。
这是典型的JavaScript流程控制,问题的关键是如何实现任务的顺序执行。在Express有一个类似的东西叫中间件,这个中间件和我们这里的吃饭、睡觉等任务很类似,每一个中间件执行完成后会调用next()函数,这个函数用来调用下一个中间件。
对于这个问题,我们也可以利用相似的思路来解决,首先创建一个任务队列,然后利用next()函数来控制任务的顺序执行:
1.2 队列方式实现
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!"); },time*1000); } })(time); this.tasks.unshift(fn); return this; } /* 封装 */ function LazyMan(name){ return new _LazyMan(name); }
1.3 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; }) }) }, sleepFirst: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'); },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~~