LazyMay:实现同步和异步任务的顺序执行

在掘金看到的文章,流程控制同步和异步任务的顺序执行,收益匪浅,工作中能用到。

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~~

原文地址:https://juejin.im/post/59c0da1f5188252c24747715