Advanced-Frontend / Daily-Interview-Question

我是依扬(木易杨),公众号「高级前端进阶」作者,每天搞定一道前端大厂面试题,祝大家天天进步,一年后会看到不一样的自己。
https://muyiy.cn/question/
27.34k stars 3.29k forks source link

第 56 题:要求设计 LazyMan 类,实现以下功能。 #98

Open zeroone001 opened 5 years ago

zeroone001 commented 5 years ago
LazyMan('Tony');
// Hi I am Tony

LazyMan('Tony').sleep(10).eat('lunch');
// Hi I am Tony
// 等待了10秒...
// I am eating lunch

LazyMan('Tony').eat('lunch').sleep(10).eat('dinner');
// Hi I am Tony
// I am eating lunch
// 等待了10秒...
// I am eating diner

LazyMan('Tony').eat('lunch').eat('dinner').sleepFirst(5).sleep(10).eat('junk food');
// Hi I am Tony
// 等待了5秒...
// I am eating lunch
// I am eating dinner
// 等待了10秒...
// I am eating junk food

Answer

class LazyManClass {
    constructor(name) {
        this.taskList = [];
        this.name = name;
        console.log(`Hi I am ${this.name}`);
        setTimeout(() => {
            this.next();
        }, 0);
    }
    eat (name) {
        var that = this;
        var fn = (function (n) {
            return function () {
                console.log(`I am eating ${n}`)
                that.next();
            }
        })(name);
        this.taskList.push(fn);
        return this;
    }
    sleepFirst (time) {
        var that = this;
        var fn = (function (t) {
            return function () {
                setTimeout(() => {
                    console.log(`等待了${t}秒...`)
                    that.next();
                }, t * 1000);  
            }
        })(time);
        this.taskList.unshift(fn);
        return this;
    }
    sleep (time) {
        var that = this
        var fn = (function (t) {
            return function () {
                setTimeout(() => {
                    console.log(`等待了${t}秒...`)
                    that.next();
                }, t * 1000); 
            }
        })(time);
        this.taskList.push(fn);
        return this;
    }
    next () {
        var fn = this.taskList.shift();
        fn && fn();
    }
}
function LazyMan(name) {
    return new LazyManClass(name);
}
LazyMan('Tony').eat('lunch').eat('dinner').sleepFirst(5).sleep(4).eat('junk food');
lvwxx commented 5 years ago
class LazyManClass {
  constructor(name) {
    this.name = name
    this.queue = []
    console.log(`Hi I am ${name}`)
    setTimeout(() => {
      this.next()
    },0)
  }

  sleepFirst(time) {
    const fn = () => {
      setTimeout(() => {
        console.log(`等待了${time}秒...`)
        this.next()
      }, time)
    }
    this.queue.unshift(fn)
    return this
  }

  sleep(time) {
    const fn = () => {
      setTimeout(() => {
        console.log(`等待了${time}秒...`)
        this.next()
      },time)
    }
    this.queue.push(fn)
    return this
  }

  eat(food) {
    const fn = () => {
      console.log(`I am eating ${food}`)
      this.next()
    }
    this.queue.push(fn)
    return this
  }

  next() {
    const fn = this.queue.shift()
    fn && fn()
  }
}

function LazyMan(name) {
  return new LazyManClass(name)
}
csm0912 commented 5 years ago
function LazyMan(name){
    var lazy = new Lazy(name);
    lazy.init();
    return lazy;
}
function Lazy(name){
    this.name = name;
    this.fns  = [];
    this.init = function(){
        var _this = this;
        _this.print("I am "+this.name);
        setTimeout(function(){
            _this.next();
        }, 0);
    };
    this.eat = function(food){
        var _this = this;
        _this.fns.push(function(){
            _this.print("I am eating "+food);
            _this.next();
        });
        return this;
    };
    this.sleep= function(time){
        var _this = this;
        _this.fns.push(function(){
            setTimeout(function(){
                _this.print("等待了" + time + "秒");
                _this.next();
            }, 1000*time);
        });
        return this;
    };
    this.sleepFirst = function(time){
        var _this = this;
        _this.fns.unshift(function(){
            setTimeout(function(){
                _this.print("等待了" + time + "秒");
                _this.next();
            }, 1000*time)
        });
        return this;
    };
    this.print = function(something){
        console.log(something);
    };
    this.next = function(){
        this.fns.length>0 && this.fns.shift()();
    };
}
jefferyE commented 5 years ago
function LazyMan (name) {
  class Man {
   constructor (name) {
     this._queues = []
     console.log(`Hi I am ${name}`)
     Promise.resolve().then(() => {
      this.next()
    })
    return this
   }

   _sleep = (time) => {
     return new Promise(resolve => setTimeout(resolve, time * 1000))
   }

   eat (type) {
     this._queues.push(() => {
       console.log(`I am eating ${type}`)
       this.next();
     })
     return this
   }
   sleepFirst (time) {
     this._queues.unshift(() => {
       this._sleep(time).then(() => {
         console.log(`等待了${time}秒`)
         this.next()
       })
    })
    return this
   }

   sleep (time) {
     this._queues.push(() => {
       this._sleep(time).then(() => {
         console.log(`等待了${time}秒`)
        this.next()
      })
     })
     return this
   }

   next () {
     const fn = this._queues.shift();
     fn && fn()
  }
 }

  return new Man(name)
}

LazyMan('Tom');

LazyMan('Tom').sleep(10).eat('lunch')

LazyMan('Tom').eat('lunch').sleep(10).eat('dinner')

LazyMan('Tom').eat('lunch').eat('dinner').sleepFirst(5).sleep(10).eat('junk food')
Yanhua67 commented 5 years ago

class LazyMan {
  constructor(name) {
    this.task = [];
    this.task.push(
      () => {
        console.log(`My name is ${name}`);
        this.next();
      }
    )
    setTimeout(() => { this.next() })
  }
  eat(val) {
    this.task.push(
      () => {
        console.log(`吃 ${val}`);
        this.next();
      }
    )
    return this;
  }
  sleep(sec) {
    this.task.push(
      () => {
        setTimeout(() => {
          console.log(`睡 ${sec} 秒`);
          this.next();
        }, sec * 1000);
      }
    )
    return this;
  }
  next() {
    let fn = this.task.shift();
    fn && fn();
  }
}

let lazyCzh = new LazyMan('czh');
lazyCzh.eat('apple').eat('banana').sleep(3).eat('orange');
mengsixing commented 5 years ago

Proxy 版本

function LazyMan(username) {
  console.log(' Hi I am ' + username);

  var temp = {
    taskList: [],
    sleepFirst(timeout) {
      return () => {
        setTimeout(() => {
          console.log(`等待了${timeout}秒...`);
          this.next();
        }, timeout * 1000);
      };
    },
    sleep(timeout) {
      return () => {
        setTimeout(() => {
          console.log(`等待了${timeout}秒...`);
          this.next();
        }, timeout * 1000);
      };
    },
    eat(type) {
      return () => {
        console.log(`I am eating ${type}`);
        this.next();
      };
    },
    next() {
      var fn = this.taskList.shift();
      fn && fn();
    }
  };

  var proxy = new Proxy(temp, {
    get(target, key, receiver) {
      return function(...rest) {
        if (key === 'sleepFirst') {
          target.taskList.unshift(target[key](rest));
        } else {
          target.taskList.push(target[key](rest));
        }
        return receiver;
      };
    }
  });

  setTimeout(() => {
    temp.next();
  }, 0);
  return proxy;
}
LazyMan('Tony').eat('lunch').eat('dinner').sleepFirst(5).sleep(10).eat('junk food');
// Hi I am Tony
// 等待了5秒...
// I am eating lunch
// I am eating dinner
// 等待了10秒...
// I am eating junk food
happywanghao commented 5 years ago
    function LazyMan(name) {
      console.log("Hi I am Tony");
      return {
        sleepTime: 0,
        firstSleepTime: 0,
        sleep: function(second) {
          let time = 0;
          function logText() {
            setTimeout(() => {
              console.log("等待了十秒...");
            }, time);
          }

          setTimeout(() => {
            time = second * 1000 + this.firstSleepTime * 1000;
            logText();
          }, 0);

          this.sleepTime = this.sleepTime + second;
          return this;
        },
        eat: function(meal) {
          let time = this.sleepTime * 1000;

          function logText() {
            setTimeout(() => {
              console.log("I am eating " + meal);
            }, time);
          }
          setTimeout(() => {
            time = time + this.firstSleepTime * 1000;
            logText();
          }, 0);
          return this;
        },

        sleepFirst: function(second) {
          let time = second * 1000;
          setTimeout(() => {
            console.log("等待了5秒");
          }, time);
          this.firstSleepTime = second;
          return this;
        }
      };
    };
BaconZhang commented 5 years ago

写个链表实现

class Node {
  constructor(func = null) {
    this.func = func;
    this.next = null;
  }

  async exec() {
    if (this.func) {
      await this.func();
    }
    if (this.next && this.next.func) {
      this.next.exec();
    }
  }
}

function delayFunc(delay) {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log(`等待了${delay}秒...`);
      resolve();
    }, delay * 1000);
  });
}

class Lazy {
  constructor(name) {
    this.name = name;
    this.head = new Node();
    this.current = this.head;
    Promise.resolve().then(() => this.head.exec());
  }

  eat(sth) {
    const log = () => {
      console.log("I am eating " + sth);
    };
    this.current.next = new Node(log);
    this.current = this.current.next;
    return this;
  }

  sleep(delay) {
    this.current.next = new Node(() => delayFunc(delay));
    this.current = this.current.next;
    return this;
  }

  sleepFirst(delay) {
    let head = new Node(() => delayFunc(delay));
    if (!this.head.func) {
      head.next = this.head.next;
    } else {
      head.next = this.head;
    }
    this.head = head;
    return this;
  }
}

function LazyMan(name) {
  console.log("I am " + name);
  return new Lazy(name);
}

LazyMan('Tony').eat('lunch').eat('dinner').sleepFirst(5).sleep(10).eat('junk food');
Seasonley commented 5 years ago
function LazyMan(name){
 return new class LazyMan{
  constructor(name){
   this.tasks=[]
   this.hello(name)
   setTimeout(this.next.bind(this),0)
  }
  log(str){console.log(str);return true}
  next(){
   var fn=this.tasks.shift()
   fn&&fn.apply(this)
  }
  hello(name){
   this.tasks.push(()=>this.log('hello '+name)&&this.next())
   return this
  }
  sleepfirst(time){
   this.tasks.unshift(()=>this.log('sleep '+time)&&setTimeout(this.next.bind(this),time))
   return this
  }
  eat(what){
   this.tasks.push(()=>this.log('eat '+what)&&this.next())
   return this
  }
  sleep(time){
   this.tasks.push(()=>this.log('sleep '+time)&&setTimeout(this.next.bind(this),time))
   return this
  }
 }(name)
}
lulusir commented 5 years ago
/**
 * 普通 eat方法, 存放在立即执行队列中, 用settimeout来执行
 * sleep 方法  每次调用这个方法, 都单独开一个定时器, 把新的任务加入到这个队列
 * sleepFirst方法 把立即执行的任务队列的任务 塞到新的定时器任务队列
 */

class LazymanClass {
  constructor (_name = '') {
    this._immediateTask = []
    this._immediateTimer = null
    this._sleepTaskMap = {}
    this._curSleepTaskKey = null
    this._log(`Hi i am ${_name}`)
  }

  eat (meal) {
    // 添加新任务之前 清空之前的 立即定时器
    this._immediateTimer && clearTimeout(this._immediateTimer)

    const _eat = (meal) => {
      this._log(`i am eating ${meal}`)
    }

    if (this._curSleepTaskKey === null) {
      this._immediateTask.push(_eat.bind(this, meal))
    } else {
      this._sleepTaskMap[this._curSleepTaskKey].push(_eat.bind(this, meal))
    }

    this._immediateTimer = setTimeout(() => {
      this._runImmeadiateTask()
    })
    return this
  }

  sleep (second) {
    const key = Math.random()
    this._curSleepTaskKey = key
    this._sleepTaskMap[this._curSleepTaskKey] = []
    setTimeout(() => {
      this._log(`等待了${second}秒`);
      this._runSleepTask(key)
    }, second * 1000)
    return this
  }

  sleepFirst (second) {
    const key = Math.random()
    this._curSleepTaskKey = key
    this._sleepTaskMap[key] = []
    this._immediateTask.map(task => {
      this._sleepTaskMap[key].push(task)
    })
    this._immediateTask = []

    setTimeout(() => {
      this._log(`等待了${second}秒`);
      this._runSleepTask(key)
    }, second * 1000)
    return this
  }

  _runImmeadiateTask () {
    this._immediateTask.map(task => {
      typeof task === 'function' && task()
    })
    this._immediateTask = []
  }

  _runSleepTask (key) {
    this._sleepTaskMap[key].map(task => {
      typeof task === 'function' && task()
    })
    this._sleepTaskMap[key] = []
  }

  _log(str) {
    console.log(str)
  }
}

function LazyMan(name) {
  return new LazymanClass(name)
}

const test = () => {
  // LazyMan('Tony')

  // LazyMan('Tony').eat('lunch')

  // LazyMan('Tony').sleep(2).eat('lunch')

  // LazyMan('Tony').eat('lunch').sleep(2).eat('diner')

  LazyMan('Tony').eat('lunch').eat('diner').sleepFirst(1).sleep(2).eat('junk food')
}

test()
Banlangenn commented 5 years ago

class LazyManClass {
    constructor(props){
        this.sub = []
        console.log(`Hi I am ${props}`)
        setTimeout(()=>{
            this.start()
        },0)
    }
    eat (params){
        this.sub.push(function(){
            console.log(`I am eating ${params}`)
        })
        return this
    }
    sleepFirst(s){
        this.sub.unshift(this.delay(s))
        // 这边还没有返回  同步就继续执行了
       return this
    }
    delay(s) {
        return () => {
            return new Promise(resolve => {
                setTimeout(function () {
                    console.log(`等待了${s}秒...`)
                    resolve()
                },s * 1000)
            })
        }
    }
    sleep (s){
        this.sub.push(this.delay(s))
        // 这边还没有返回  同步就继续执行了
       return this
    }
    async start(){
        for (const iterator of this.sub) {
            await iterator()
        }
    }
}
function LazyMan(props) {
    return  new LazyManClass(props)
}
LazyMan('Tony').eat('lunch').eat('diner').sleepFirst(1).sleep(2).eat('junk food')
// Hi I am Tony
// 等待了5秒...
// I am eating lunch
// I am eating dinner
// 等待了10秒...
// I am eating junk food
yiqingfeng commented 5 years ago
function LazyMan(name) {
    if (!(this instanceof LazyMan)) {
        return new LazyMan(name);
    }
    this.name = name;
    this.delayTime = 0;
    this.tasks = [];
    console.log(`Hi I am ${this.name}`);
    this._ready();
}

LazyMan.prototype._ready = function () {
    setTimeout(() => {
        setTimeout(() => {
            console.log(`等待了${this.delayTime}秒...`);
            this._doTasks();
        }, this.delayTime * 1000);
    }, 0);
}

LazyMan.prototype._doTasks = function () {
    const tasks = this.tasks;
    if (tasks.length === 0) return;
    const {delayTime, callback} = tasks[0];
    setTimeout(() => {
        callback && callback();
        tasks.shift();
        this._doTasks();
    }, delayTime);
}

LazyMan.prototype.eat = function (foods) {
    this.tasks.push({
        delayTime: 0,
        callback() {
            console.log(`I am eating ${foods}`);
        },
    });
    return this;
}

LazyMan.prototype.sleep = function (seconds) {
    if (seconds >= 0) {
        this.tasks.push({
            delayTime: seconds * 1000,
            callback() {
                console.log(`等待了${seconds}秒...`)
            }
        });
    }
    return this;
}

LazyMan.prototype.sleepFirst = function (seconds) {
    if (seconds > 0) {
        this.delayTime = seconds;
    }
    return this;
}
Mistyyyy commented 5 years ago
function LazyMan(name) {
  if (this instanceof LazyMan) {
    this.task = [];
    this.log('Hi I am '+ name);
    setTimeout(() => {
      this.next();
    }, 0);
  }
  return (this instanceof LazyMan) && this || new LazyMan(name);
}

LazyMan.prototype.log = console.log;

LazyMan.prototype.next = function() {
  const [fn, ...tail] = this.task;
  fn && (this.task = tail) && fn();
}

LazyMan.prototype.eat = function(food) {
  this.task = [
    ...this.task,
    () => {
      this.log('I am eating '+ food);
      this.next();
    }
  ]
  return this;
}

LazyMan.prototype.sleep = function(timeout) {
  this.task = [
    ...this.task,
    () => {
      setTimeout(() => {
        this.next();
      }, timeout * 1000);
    }
  ]
  return this;
}

LazyMan.prototype.sleepFirst = function(timeout) {
  this.task = [
    () => {
      setTimeout(() => {
        this.next();
      }, timeout * 1000)
    },
    ...this.task
  ]
  return this;
}

LazyMan('Tony').eat('lunch').eat('dinner').sleepFirst(1).sleep(2).eat('junk food');
zhuzhh commented 5 years ago

// 写一个 setTimeout 就可以了。

class lazyManClass {
    constructor(name){
        this.taskList = []
        this.init(name)
    }

    init(name){
        var _this = this
        this.taskList.push({
            time: 0, // 是否是异步
            fn:(function(s){ // 函数自执行
                    return function() {
                        _this.exec()
                        console.log('Hi I am ', s)
                    }
                })(name)
        })
        this.exec()
        return this
    }
    eat(str) {
        var _this = this
        this.taskList.push({
            time: 0,
            fn: (function(s){
                    return function() {
                        _this.exec()
                        console.log('I am eating ', s)
                    }
                })(str)
        })

        return this
    }
    sleep(t){
        var _this = this
        this.taskList.push({
            time: t * 1000,
            fn: (function(time){
                    return function() {
                        _this.exec()
                        console.log(`等待了${time}秒...`) 
                    }
                })(t)
        })
        return this
    }
    sleepFirst(t) {
        var _this = this
        this.taskList.unshift({
            time: t * 1000,
            fn: (function(time){
                    return function() {
                        _this.exec()
                        console.log(`等待了${time}秒...`) 
                    }
                })(t)
        })
        return this
    }
    exec(){
        var obj = this.taskList.shift()
        if(!obj) {
            return   // 最后一个fn里面,也会执行 _this.exec(),这个时候taskList已经空了
        }
        setTimeout(() => {
            obj.fn && obj.fn.call(this)
        }, obj.time || 0)

    }
}

function LazyMan(name) {
    return new lazyManClass(name)
}   
neofanfei commented 5 years ago

class LazyMan {
    constructor (user) {
        this.timeList = [];
        console.log(`Hi I am ${user}`)
        Promise.resolve().then(res => this.next());
    }
    eat (res) {
        var fn =  () => {
            console.log(`I am eating ${res}`);
            this.next();
        }
        this.timeList.push(fn);
        return this;
    }
    sleep (time) {
        var fn = res => {
            setTimeout(res => {
                console.log(`等待了${time}秒`);
                this.next();
            },time*1000)
        }
        this.timeList.push(fn);
        return this;
    }
    next () {
        var fn = this.timeList.shift();
        fn && fn();
    }
    sleepFrist (time) {
        var fn = res => {
            setTimeout(res => {
                console.log(`先等待了${time}秒`);
                this.next();
            },time*1000)
        }
        this.timeList.unshift(fn);
        return this;
    }
}
function lazyman (res) {
    return new LazyMan(res)
}
_//lazyman('静静').sleep(2).eat('饺子').sleep(3).eat('面').sleepFrist(1).eat('others')
//Hi I am 静静
//先等待了1秒
//等待了2秒
//I am eating 饺子
//等待了3秒
//I am eating 面
//I am eating others_
yeyan1996 commented 5 years ago
class LazyMan {
    constructor(name) {
        this.tasks = []
        const task = () => {
            console.log(name)
        }
        this.tasks.push(task)
        setTimeout(() => {
            this.exec()
        })
    }

    sleep(time) {
        const task = () => new Promise(resolve => {
            setTimeout(resolve, time)
        })
        this.tasks.push(task)
        return this
    }
    sleepFirst(time) {
        const task = () => new Promise(resolve => {
            setTimeout(resolve, time)
        })
        let originTask = this.tasks.shift()
        this.tasks.unshift(originTask,task)
        return this
    }
    eat(food) {
        const task = () => {
            console.log(food)
        }
        this.tasks.push(task)
        return this
    }

     async exec() {
        for (let task of this.tasks) {
            await task()
        }
    }
}
jayPiece commented 5 years ago
    function LazyManClass(name){
        this.name = name;
        console.log('Hi I am '+name);
        //存储行为列表
        this.taskList = [];

        //声明类行为执行函数
        this.doTask = function(){
            var fn = this.taskList.shift();
            if(fn){
                fn();
            }
        };

        //异步执行默认行为函数
        var _this = this;
        setTimeout(function(){
            _this.doTask();
        },0);

    }

    LazyManClass.prototype.eat = function(food){
        var _this = this;
        var fn = function(){
            console.log('I am eating '+food);
            _this.doTask();
        }

        //维护行为列表
        this.taskList.push(fn);
        return this;
    }

    LazyManClass.prototype.sleepFirst = function(time){
        var _this = this;
        var fn = function(){
            setTimeout(function(){
                console.log('等待了 '+time+'秒...');
                _this.doTask();
            },time*1000)
        }

        //维护行为列表
        this.taskList.unshift(fn);
        return this;
    }

    LazyManClass.prototype.sleep = function(time){
        var _this = this;
        var fn = function(){
            setTimeout(function(){
                console.log('等待了 '+time+'秒...');
                _this.doTask();
            },time*1000)
        }

        //维护行为列表
        this.taskList.push(fn);
        return this;
    }

    //函数调用返回实例化对象
    function LazyMan(name){
        return new LazyManClass(name);
    }
    LazyMan('Tony').eat('lunch').eat('dinner').sleepFirst(5).sleep(10).eat('junk food');
   //感谢各位前辈们的代码思路,自己用es5写了一遍
twosugar commented 5 years ago

有大佬能解答下next()调用的时机吗,这个运行流程我有点看不懂=。=

jayPiece commented 5 years ago

有大佬能解答下next()调用的时机吗,这个运行流程我有点看不懂=。=

我不是大佬,但我理解的next()应该是在LazyMan('Toney')的时候异步执行的,因为异步,所以它还没干活的时候,链式调用早结束了,taskList也把要干的活按照规则存好了,随后它开始干活,由于taskList中每个活函数执行完后又主动调用了一次next(),所以能一直把taskList中的活干完

twosugar commented 5 years ago

有大佬能解答下next()调用的时机吗,这个运行流程我有点看不懂=。=

我不是大佬,但我理解的next()应该是在LazyMan('Toney')的时候异步执行的,因为异步,所以它还没干活的时候,链式调用早结束了,taskList也把要干的活按照规则存好了,随后它开始干活,由于taskList中每个活函数执行完后又主动调用了一次next(),所以能一直把taskList中的活干完

如果真是这样那就好理解了,谢谢解答!!

RichardPear commented 5 years ago
function LazyMan(name) {
    console.log(`I am ${name}`);
    var task = [];
    function execute() {
    var fn = task.shift();
        fn && fn(); 
    }
   // delay execute
    setTimeout(function() {
        execute();
    }, 0);
    function _sleep(n = 0) {
        console.log(`${name} is sleeping ${n}`);
        setTimeout(function() {
            execute();
        }, n * 1000);
    }
    function _eat(food) {
        console.log(`${name} is eating ${food}`);
        execute();
    }
    var obj = {
    sleep: function() {
        task.push(_sleep.bind(null, ...arguments));
        return obj;
        },
    eat: function() {
        task.push(_eat.bind(null, ...arguments));
        return obj;
    },
        sleepFirst: function() {
        task.unshift(_sleep.bind(null, ...arguments));
        return obj;
    }
    };
    return obj;
}
LazyMan('Tony').eat('lunch').eat('dinner').sleepFirst(5).sleep(10).eat('junk food');
lzbSun commented 5 years ago

有大佬能解答下next()调用的时机吗,这个运行流程我有点看不懂=。=

我不是大佬,但我理解的next()应该是在LazyMan('Toney')的时候异步执行的,因为异步,所以它还没干活的时候,链式调用早结束了,taskList也把要干的活按照规则存好了,随后它开始干活,由于taskList中每个活函数执行完后又主动调用了一次next(),所以能一直把taskList中的活干完

如果真是这样那就好理解了,谢谢解答!!

在constructor中, next()方法执行在 setTimeout 中,setTimeout 是一个task,当 js 运行到 setTimeout 的时候,会把他放到 task 任务队列中,等到所有的同步的任务执行完后,就会执行setTimeout 中的this.next()方法 , 这个问题的关键问题是 js 的事件队列问题 。 你可以参考这个链接详细了解 https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/7?from=groupmessage&isappinstalled=0

lzbSun commented 5 years ago

@GuidingStarKe

twosugar commented 5 years ago

有大佬能解答下next()调用的时机吗,这个运行流程我有点看不懂=。=

我不是大佬,但我理解的next()应该是在LazyMan('Toney')的时候异步执行的,因为异步,所以它还没干活的时候,链式调用早结束了,taskList也把要干的活按照规则存好了,随后它开始干活,由于taskList中每个活函数执行完后又主动调用了一次next(),所以能一直把taskList中的活干完

如果真是这样那就好理解了,谢谢解答!!

在constructor中, next()方法执行在 setTimeout 中,setTimeout 是一个task,当 js 运行到 setTimeout 的时候,会把他放到 task 任务队列中,等到所有的同步的任务执行完后,就会执行setTimeout 中的this.next()方法 , 这个问题的关键问题是 js 的事件队列问题 。 你可以参考这个链接详细了解 https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/7?from=groupmessage&isappinstalled=0

首先谢谢解答和分享!

bohaixv commented 5 years ago
class LazyMan {
  constructor(name){
    this.name = name
    this.eventQueue = []
    console.log('Hi , I am %s.', name )
    setTimeout(()=>{
      this.drain()
    },0)
  }
  eat(meal){
    const fn = function _eat(next){
      console.log('I am eating %s.',meal)
      next && next()
    }
    this.eventQueue.push(fn)
    return this
  }
  sleep(second){
    const fn = function _sleep(next){
      console.log('I am waiting %d second.', second)
      setTimeout(()=>{
        next && next()
      },second*1000)
    }
    this.eventQueue.push(fn)
    return this
  }
  sleepFirst(second){
    const fn = function _sleepFirst(next){
      console.log('I am waiting %d second.', second)
      setTimeout(()=>{
        next && next()
      },second*1000)
    }
    this.eventQueue.splice(0,0,fn)
    return this
  }
  drain(){
    const fnComposed = this.eventQueue.reduceRight(function _reduceRight(pre,next){
      return function (){
        return next(pre)
      }
    },)
    fnComposed()
  }
}
const TonyStark =new LazyMan('Tony').eat('lunch').sleep(10).eat('dinner');
pengcc commented 5 years ago

用了async,不需callback, 下面是两种方法放一起了。 第一种方法注释掉了,调用任务eat或者sleep或者sleepFirst时,把任务的data存放队列里,在init的方法里异步调用async executeTasks, 任务存放是同步的,先于异步的方法executeTasks执行完,在executeTasks方法里依次让任务data出列,然后执行相应的任务。

第二种是,使用next方法,每个任务最后都会调用一次next方法,把每个任务都压入队列,next的一次调用也是异步的,晚于同步的任务入列执行,next方法里让当前的任务入列,并执行。

function LazyMan(name) {
    return new LazyManClass(name);
}

class LazyManClass {
    constructor(name) {
        this.taskList = [];
        // used for method executeTasks
        // this.taskDataList = [];
        this.init(name);
    }

    init(name) {
        this.printLog('name', name);
        setTimeout(() => {
            // the alternative solution
            //this.executeTasks();
            this.next();
        }, 0);
    }

    printLog(type, data) {
        const LOG_MAP = {
            'name': `Hi, I am ${data}`,
            'eat': `I am eating ${data}.`,
            'sleep': `Waited for ${data} seconds.`,
            'error': `Got something wrrong: ${data}.`
        };
        console.log(LOG_MAP[type]);
    }

    delayPromise(t) {
        return new Promise(resolve => {
            setTimeout(() => {
                resolve();
            }, t * 1000);
        });
    };

    createTaskData(type, data) {
        return { type, data };
    }

    createTask(type, data) {
        return async () => {
            if (type === 'sleep') {
                try {
                    await this.delayPromise(data);
                }
                catch(e) {
                    this.printLog('error', e);
                }
            }
            this.printLog(type, data);
            this.next();
        }
    }

    addTask(type, data, isFirst=false) {
        if (isFirst) {
            // used for method executeTasks
            // this.taskList.unshift(this.createTask(type, data));
            this.taskList.unshift(this.createTask(type, data));
        } else {
            // used for method executeTasks
            // this.taskList.unshift(this.createTask(type, data));
            this.taskList.push(this.createTask(type, data));
        }
    }

    eat(str) {
        this.addTask('eat', str);
        return this;
    }

    sleep(t) {
        this.addTask('sleep', t);
        return this;
    }

    sleepFirst(t) {
        this.addTask('sleep', t, true);
        return this;
    }

    next() {
        if (this.taskList.length > 0) {
            let task = this.taskList.shift();
            task && task();
        }
    }
    async executeTasks() {
        let taskDataList = this.taskDataList;
        while (taskDataList.length > 0) {
            let { type, data } = taskDataList.shift();
            if (type === 'sleep') {
                try {
                    await this.delayPromise(data);
                }
                catch(e) {
                    this.printLog('error', e);
                }
            }
            this.printLog(type, data);
        }
    }
}
kangkai124 commented 5 years ago

image

有没有大佬解答一下,这里传立即执行函数的返回值(函数)和直接传红字部分的函数,有什么区别吗

zhuzhh commented 5 years ago

var fn = (n) => { console.log(n) }

你这样写,n的找不到name 你这样写,fn是个函数,而fn执行的时候是在next方法里执行的。而fn需要的的参数,你怎么获取到name呢?

在 2019-04-29 18:25:18,"kk" notifications@github.com 写道:

有没有大佬解答一下,这里传立即执行函数的返回值(函数)和直接传红字部分的函数,有什么区别吗

— You are receiving this because you commented. Reply to this email directly, view it on GitHub, or mute the thread.

YagamiNewLight commented 5 years ago

image

有没有大佬解答一下,这里传立即执行函数的返回值(函数)和直接传红字部分的函数,有什么区别吗

立即执行函数的作用是在内部函数还没有执行的时候就已经为内部函数绑定好了对应参数的值,如果不用立即函数的话也可以用bind方法

var name = '小明'
var fn = function (n){
 console.log(`I am eating ${n}`)
 this.next()
}.bind(null, name)

上面的代码实际上是让fn等于下面这个函数:
function (){
 console.log(`I am eating 小明`)
 this.next()
}

这样一来无论你在什么地方执行fn都不需要传参数了,直接fn()不用参数也能达到和普通fn的fn(name )效果一样了

liujuntao123 commented 5 years ago

不用类的实现,js里基本用类实现的用对象也可以实现。

LazyMan('Tony')
// Hi I am Tony

LazyMan('Tony')
  .sleep(10)
  .eat('lunch')
// Hi I am Tony
// 等待了10秒...
// I am eating lunch

LazyMan('Tony')
  .eat('lunch')
  .sleep(10)
  .eat('dinner')
// Hi I am Tony
// I am eating lunch
// 等待了10秒...
// I am eating diner

LazyMan('Tony')
  .eat('lunch')
  .eat('dinner')
  .sleepFirst(5)
  .sleep(10)
  .eat('junk food')
// Hi I am Tony
// 等待了5秒...
// I am eating lunch
// I am eating dinner
// 等待了10秒...
// I am eating junk food

下面是实现

let man = {
  queue: []
}

function LazyMan(name) {
  console.log(`Hi I am ${name}`)
  setTimeout(() => {
    man.next()
  }, 0)
  return man
}

man.eat = food => {
  man.queue.push(() => {
    console.log(`I am eating ${food}`)
    man.next()
  })
  return man
}

man.sleep = timeout => {
  man.queue.push(() => {
    setTimeout(() => {
      console.log(`等待了${timeout}秒`)
      man.next()
    }, timeout * 1000)
  })
  return man
}

man.sleepFirst = timeout => {
  man.queue.unshift(() => {
    setTimeout(() => {
      console.log(`等待了${timeout}秒`)
      man.next()
    }, timeout * 1000)
  })
  return man
}

man.next = () => {
  if (man.queue.length > 0) {
    man.queue.shift()()
  }
}
dguoqing commented 5 years ago
const LazyMan = name => new class Cp {
            constructor(name) {
                this.name = name;
                this._queues = []
                console.log(` Hi I am ${name}`)
                Promise.resolve().then(() => {
                    this.next()
                })
                // return this
            }
            static _sleep = time => new Promise(resolve => setTimeout(resolve, time * 1000))
            eat(ft) {
                this._queues.push(() => {
                    console.log(`I am eating ${ft}`)
                    this.next()
                })
                return this
            }
            sleepFirst(time) {
                this._queues.unshift(async () => {
                    await Cp._sleep(time)
                    console.log(`等待了 ${time} 秒`)
                    this.next()
                })
                return this
            }
            sleep(time) {
                this._queues.push(async () => {
                    await Cp._sleep(time)
                    console.log(`等待了 ${time} 秒`)
                    this.next()
                })
                return this
            }
            next() {
                const fn = this._queues.shift();
                fn && fn()
            }
        }(name)

    LazyMan('Tom');

    LazyMan('Tom').sleep(10).eat('lunch')

    LazyMan('Tom').eat('lunch').sleep(3).eat('dinner')

    LazyMan('Tom').eat('lunch').eat('dinner').sleepFirst(5).sleep(10).eat('junk food')
logan70 commented 5 years ago
class LazyMan {
  constructor(name) {
    this.promise = Promise.resolve().then(() => console.log(`Hi I am ${name}`))
  }
  sleepFirst(time) {
    this.promise = this.promise.then(() => new Promise(resolve => setTimeout(resolve, time * 1000)))
    return this
  }
  eat(name) {
    setTimeout(() => {
      this.promise = this.promise.then(() => console.log(`I am eating ${name}`))
    }, 0)
    return this
  }
  sleep(time) {
    setTimeout(() => {
      this.promise = this.promise.then(() => new Promise(resolve => setTimeout(resolve, time * 1000)))
    }, 0)
    return this
  }
}

不知道符不符合要求

DannySu09 commented 5 years ago
class LazyManClass {
  constructor (username) {
    this.username = username
    this.taskList = []
    this._hello()
  }

  _addTask (task, data) {
    clearTimeout(this.timer)
    this.taskList.push(() => {
      this[task](data)
    })
    this.timer = setTimeout(() => {
      this._runTask()
    })

    return this
  }

  _runTask () {
    for (let i = 0; i < this.taskList.length; i++) {
      this.taskList[i]()
    }
  }

  _insertTask (task, data, index) {
    clearTimeout(this.timer)
    this.taskList.splice(index, 0, () => {
      this[task](data)
    })
    this.timer = setTimeout(() => {
      this._runTask()
    })
    return this
  }

  registerBehave (name, action) {
    this['_' + name] = action
    this[name] = (data) => {
      const fnName = '_' + name
      if (/\w+First/.test(fnName)) {
        this._insertTask(fnName, data, 0)
      } else {
        this._addTask(fnName, data)
      }
      return this
    }
    return this
  }
  _hello () {
    console.log('Hi I am ' + this.username)
  }
}

function LazyMan (name) {
  const man = new LazyMan(name)
  return man
    .registerBehave('sleep', function (time) {
      console.log('等待了 ' + time + ' 秒种...')
    })
    .registerBehave('eat', function (mealName) {
      console.log('I am eating ' + mealName)
    })
    .registerBehave('sleepFirst', function (time) {
      console.log('等待了 ' + time + ' 秒种...')
    })
}

LazyMan('Pikachu').eat('lunch').sleep(10).sleepFirst(8)

有点长…… 但是这样行为就是可以注册的了,以后想加个 gamingFirst 之类的都可以。

shaokr commented 5 years ago

用Generator做~

function LazyMan(str = "") {
  return new (class {
    list = [];
    task;
    constructor(str) {
      this.list.push(`Hi I am ${str}`);
      this.task = this.log();
      setTimeout(() => this.task.next());
    }
    sleepFirst = t => {
      this.list.splice(1, 0, t);
      return this;
    };
    sleep = t => {
      this.list.push(t);
      return this;
    };
    eat = str => {
      this.list.push(`I am eating ${str}`);
      return this;
    };

    log = function*() {
      for (const val of this.list) {
        yield (() => {
          if (typeof val !== "number") console.log(val);
          setTimeout(() => this.task.next(), (val >> 0) * 1000);
        })();
      }
    };
  })(str);
}

LazyMan("Tony")
  .eat("lunch")
  .eat("dinner")
  .sleepFirst(5)
  .sleep(10)
  .eat("junk food");
negativeentropy9 commented 5 years ago
class ArrayInherit extends Array {
  constructor(options) {
      super(options);
      this.options=options;
  }

  push(item) {
    super.push(item);
    this.options.pushCb(item)
  }
}

class LazyMan {
  constructor(name) {
    this.name = name;
    this.sleepQueue = new ArrayInherit({pushCb: function(item) {
      setTimeout(() => {
        item.cb();
      }, item.sleepWait)
    }});

    this.queueCb = [];
    this.sleepWaitTemp = 0;

    console.log(`Hi I am ${this.name}`);
  }

  sleep(sleepWait) {
    this.sleepWaitTemp = sleepWait;

    return this;
  }

  sleepFirst(sleepFirstWait) {
    const queueCbTemp = this.queueCb;

    this.queueCb = []

    setTimeout(() => {
      queueCbTemp.forEach(cb => {
        cb();
      })

    }, sleepFirstWait)

    return this;
  }

  get hasSleeped() {
    return this.sleepWaitTemp > 0;
  }

  eat(thing) {
    function cb() {
      console.log(`I am eating ${thing}`);
    }

    if (this.hasSleeped) {
      this.queueCb.forEach(cb => {
        cb();
      })

      this.queueCb = []

      this.sleepQueue.push({
        sleepWait: this.sleepWaitTemp,
        cb: cb.bind(this, thing)
      });
    } else {
      this.queueCb.push(cb.bind(this, thing))
    }

    return this;
  }
}

var a = new LazyMan('Tom');
a.sleep(1000).eat('lunch');

var a = new LazyMan('Tom');
a.eat('lunch').sleep(1000).eat('dinner');

var a = new LazyMan('Tom');
a.eat('lunch').eat('dinner').sleepFirst(2000).sleep(5000).eat('junk food')
PatrickLh commented 5 years ago

其实题目主要考虑的点有以下几个方面:

  1. 链式调用 -> return this
  2. 链式调用终止判断 -> 我用了setTimeout会在被主线程pendding的方式,楼上的很多方案其实更好
  3. 依次执行一个异步数组 -> 同样楼上很多Promise的嵌套方案就很好,我是采用了递归,最后一次性执行
    function LazyMan (name) {
    console.log('Hi I am Tony')
    return {
    loops: [],
    tick: null,
    eat (something) {
      this.wait()
      this.loops.push(() => new Promise(resolve => {
        resolve(true)
        console.log('I am eating ' + something)
      }))
      return this
    },
    sleep (time) {
      this.wait()
      this.loops.push(() => new Promise(resolve => {
        setTimeout(() => {
          console.log(`等待了${time}秒...`)
          resolve(true)
        }, time * 1000)
      }))
      return this
    },
    sleepFirst (time) {
      this.wait()
      this.loops.unshift(() => new Promise(resolve => {
        setTimeout(() => {
          console.log(`等待了${time}秒...`)
          resolve(true)
        }, time * 1000)
      }))
      return this
    },
    wait () {
      clearTimeout(this.tick)
      this.tick = setTimeout(() => {
        this.done()
      })
    },
    done () { // 处理所有的操作
      if (this.loops.length > 0) {
        this.loops.shift()().then(() => {
          this.done()
        })
      }
    },
    }
    }
young122849 commented 5 years ago

function LazyMan (name) { let obj = Object.create(LazyMan.prototype); obj.name = name; obj.taskList = []; console.log(Hi I am ${name}); obj.next(); return obj; }

LazyMan.prototype.eat = function (message) { this.taskList.push({ type: 'eat', payload: message }); return this; };

LazyMan.prototype.sleep = function (time) { this.taskList.push({ type: 'sleep', payload: time }); return this; };

LazyMan.prototype.sleepFirst = function (time) { this.taskList.unshift({ type: 'sleep', payload: time }); return this; };

LazyMan.prototype.next = function () { const self = this; setTimeout(async function () { let task = null; while (task = self.taskList.shift()) { switch (task.type) { case 'sleep': { await new Promise((resolve) => setTimeout(resolve, task.payload * 1000)); console.log(等待了${task.payload}秒); break; } case 'eat': { console.log(I am eating ${task.payload}); break; } } } }, 0); }

yujihu commented 5 years ago
class LazyManClass {
  constructor(name) {
    this.taskList = []
    this.name = name
    console.log(`Hi I am ${name}`)
    setTimeout(() => {
      this.next()
    }, 0)
  }
  eat (name) {
    this.taskList.push(() => {
      console.log(`I am eating ${name}`)
      this.next()
    })
    return this
  }
  sleep (delay) {
    this.taskList.push(() => {
      setTimeout(() => {
        console.log(`等待了${delay}秒...`)
        this.next()
      }, delay * 1000)
    })
    return this
  }
  sleepFirst (delay) {
    this.taskList.unshift(() => {
      setTimeout(() => {
        console.log(`等待了${delay}秒...`)
        this.next()
      }, delay * 1000)
    })
    return this
  }
  next () {
    const fn = this.taskList.shift()
    fn && fn()
  }
}

function LazyMan(name) {
  return new LazyManClass(name)
}

LazyMan('Tony').eat('lunch').eat('dinner').sleepFirst(5).sleep(10).eat('junk food')
richard1015 commented 5 years ago

执行Promise队列

class Lazy {
    constructor(name) {
        console.log(`Hi I am ${name}`);
        this.task = [];
        setTimeout(() => {
            this.execQueue(0);
        }, 0);
    }
    execQueue(index) {
        var that = this;
        if (that.task.length && index < that.task.length) {
            that.task[index]().then(res => {
                if (res) {
                    that.execQueue(++index);
                }
            })
        }
    }
    sleep(s) {
        this.task.push(() => {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    console.log(`等待了${s}秒...`)
                    resolve(true)
                }, s * 1000);
            })
        })
        return this;
    }
    sleepFirst(s) {
        this.task.unshift(() => {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    console.log(`等待了${s}秒...`)
                    resolve(true)
                }, s * 1000);
            })
        })
        return this;
    }
    eat(str) {
        this.task.push(() => {
            return new Promise((resolve, reject) => {
                console.log(`I am eating ${str}`);
                resolve(true);
            });
        })
        return this;
    }
}
function LazyMan(name) {
    return new Lazy(name);
}
// LazyMan('Tony').sleep(10).eat('lunch');
// LazyMan('Tony').eat('lunch').sleep(10).eat('dinner');
// LazyMan('Tony').eat('lunch').eat('dinner').sleepFirst(5).sleep(10).eat('junk food');
zwmmm commented 5 years ago
LazyMan('Tony');
// Hi I am Tony

LazyMan('Tony').sleep(10).eat('lunch');
// Hi I am Tony
// 等待了10秒...
// I am eating lunch

LazyMan('Tony').eat('lunch').sleep(10).eat('dinner');
// Hi I am Tony
// I am eating lunch
// 等待了10秒...
// I am eating diner

LazyMan('Tony').eat('lunch').eat('dinner').sleepFirst(5).sleep(10).eat('junk food');
// Hi I am Tony
// 等待了5秒...
// I am eating lunch
// I am eating dinner
// 等待了10秒...
// I am eating junk food

Answer

class LazyManClass {
    constructor(name) {
        this.taskList = [];
        this.name = name;
        console.log(`Hi I am ${this.name}`);
        setTimeout(() => {
            this.next();
        }, 0);
    }
    eat (name) {
        var that = this;
        var fn = (function (n) {
            return function () {
                console.log(`I am eating ${n}`)
                that.next();
            }
        })(name);
        this.taskList.push(fn);
        return this;
    }
    sleepFirst (time) {
        var that = this;
        var fn = (function (t) {
            return function () {
                setTimeout(() => {
                    console.log(`等待了${t}秒...`)
                    that.next();
                }, t * 1000);  
            }
        })(time);
        this.taskList.unshift(fn);
        return this;
    }
    sleep (time) {
        var that = this
        var fn = (function (t) {
            return function () {
                setTimeout(() => {
                    console.log(`等待了${t}秒...`)
                    that.next();
                }, t * 1000); 
            }
        })(time);
        this.taskList.push(fn);
        return this;
    }
    next () {
        var fn = this.taskList.shift();
        fn && fn();
    }
}
function LazyMan(name) {
    return new LazyManClass(name);
}
LazyMan('Tony').eat('lunch').eat('dinner').sleepFirst(5).sleep(4).eat('junk food');

为什么要写自执行函数自执行函数,感觉有点多余

function lazyMan(name) {
    console.log(`Hi I am ${name}`);
    const obj = {
        task: [],
        next() {
            // 完成一个任务之后继续完成下面的直到没有任务为止
            const task = this.task.shift();
            task && task();
        },
        sleep(second) {
            this.task.push(() => {
                console.log(`等待${second}秒...`)
                setTimeout(() => {
                    this.next();
                }, second * 1000);
            })
            return this;
        },
        eat(name) {
            this.task.push(() => {
                console.log(`I am eating ${name}`)
                this.next();
            })
            return this;
        },
        sleepFirst(second) {
            this.task.unshift(() => {
                console.log(`等待${second}秒...`);
                setTimeout(() => {
                    this.next();
                }, second * 1000);
            })
            return this;
        }
    };

    // 先等收集任务
    setTimeout(() => {
        // 收集好之后开始第一个任务
        obj.next();
    })
    return obj;
}

lazyMan('Tony').eat('lunch').eat('dinner').sleepFirst(5).sleep(10).eat('junk food');
NathanHan1 commented 5 years ago
      class LazyMan {
        constructor(name) {
          this.tasks = [];
          //  启动 tasks,利用 then的 异步原理拿到已经填充好的tasks
          Promise.resolve().then(() => {
            this.nextTick()
          })
          console.log(`hi i am ${name}`);
        }

        sleep(delay) {
          this.tasks.push(() => {
            setTimeout(() => {
              this.nextTick();
            }, delay * 1000);
          });

          return this;
        }

        sleepFirst(delay) {
          // unshift 插队到最前面
          this.tasks.unshift(() => {
            setTimeout(() => {
              this.nextTick();
            }, delay * 1000);
          });
          return this;
        }

        eat(what) {
          this.tasks.push(() => {
            console.log(`eating ${what}`);
            this.nextTick();
          });

          return this;
        }

        nextTick() {
          this.tasks.length && this.tasks.shift()();
        }
      }
wangliang1124 commented 5 years ago
class LazyMan {
        constructor(name) {
            this.name = name;
            console.log(`Hi, I am ${name}`)
            this.taskQueue = []
            setTimeout(() => {
                this.start()
            })
        }

        async start() {
            for (let task of this.taskQueue) {
                await task()
            }
        }

        eat(dinner) {
            this.taskQueue.push(() => {
                console.log(`I am eating ${dinner}`)
            })
            return this;
        }

        wait(time) {
            return () => new Promise(resolve => {
                setTimeout(() => {
                    console.log(`waiting ${time} second`)
                    resolve()
                }, time * 1000)
            })
        }

        sleep(time) {
            this.taskQueue.push(this.wait(time))
            return this;
        }

        sleepFirst(time) {
            this.taskQueue.unshift(this.wait(time))
            return this
        }
    }

    // new LazyMan('test1').sleep(2).eat('lunch')
    // new LazyMan('test2').eat('lunch').sleep(5).eat('dinner')
    new LazyMan('test3').eat('lunch').eat('dinner').sleepFirst(2).sleep(3).eat('junk food')
Hunterang commented 5 years ago

function LazyMan(str) { if (this.constructor !== LazyMan) { return new LazyMan(str) } this.willDoThing= [] this.firstDoing = [] this.firstSleep = false this._init(str) } LazyMan.prototype.init = function(str) { console.log(Hi I am ${str}); Promise.resolve().then( => { this.startDoing() }) return this } LazyMan.prototype.startDoing = async function() { if (this.firstSleep) { await this.firstDoing[0]() this.willDoThing.forEach( async (v) => {await v()} ) }else { this.willDoThing.forEach( async (v) => {await v()} ) } }

LazyMan.prototype.sleepFirst = function(time) { this.firstSleep = true this.firstDoing.push(async() => { await sleep(time) console.log('first sleep'+time); }) return this }

LazyMan.prototype.sleep = function(time) { this.willDoThing.push(async() => { await sleep(time) console.log('sleep'+time); }) return this }

function sleep(time) { return new Promise((a,b) => { setTimeout(()=> a() ,time) }) } LazyMan('zz').sleep(1000).sleepFirst(2000)

//备注其他功能就给willdoingThing添加fn就可以了,只是试验了一下两个sleep函数,多了一个判断

AsakuraC commented 5 years ago
function LazyMan(name) {
  console.log(`Hi I am ${name}`);

  const logArr = [];

  const executor = {
    _status: 'stoped',
    async _actor() {
      while (logArr.length) {
        const { wait, text } = logArr.shift();
        if (wait) await new Promise(resolve => setTimeout(resolve, wait));
        console.log(text);
      }
      this._status = 'stoped';
    },
    async dispatch() {
      if (this._status === 'running') return;
      this._status = 'running';
      await new Promise(resolve => setTimeout(resolve, 0));
      this._actor();
    },
  };

  return {
    eat(food) {
      logArr.push({
        wait: 0,
        text: `I am eating ${food}`,
      });
      executor.dispatch();
      return this;
    },

    sleepFirst(second) {
      logArr.unshift({
        wait: second * 1000,
        text: `等待了 ${second} 秒...`,
      });
      executor.dispatch();
      return this;
    },

    sleep(second) {
      logArr.push({
        wait: second * 1000,
        text: `等待了 ${second} 秒...`,
      });
      executor.dispatch();
      return this;
    },
  };
}
wycyftk commented 5 years ago
function LazyMan(name) {
      var o = new Object();
      o.name = name;

      console.log(`Hi I am ${o.name}`);

      o.message = [];
      setTimeout(() => {
        o.next();
      }, 0);

      o.eat = function (food) {
        var fn = function () {
          console.log(`I am eating ${food}`);
          o.next();
        }

        o.message.push(fn);
        return o;
      }

      o.sleepFirst = function (time) {
        var fn = function () {
          setTimeout(function () {
            console.log(`等待了${time}秒...`);
            o.next();
          }, time * 1000);
        }
        o.message.unshift(fn);
        return o;
      }

      o.sleep = function (time) {
        var fn = function () {
          setTimeout(function () {
            console.log(`等待了${time}秒...`);
            o.next();
          }, time * 1000);
        }
        o.message.push(fn);
        return o;
      }

      o.next = function () {
        var fn = o.message.shift();
        fn && fn();
      }
      return o;
    }

    LazyMan('Jack').eat('lunch').eat('dinner').sleepFirst(5).sleep(10).eat('junk food');
dongCode commented 5 years ago

详情

yingye commented 5 years ago
class LazyManClass {
    constructor (name) {
        this.name = name
        this.task = []
        console.log('Hi I am ', name)
        setTimeout(() => {
            this.next()
        }, 0)
    }
    eat (str) {
        this.task.push(() => {
            console.log('I am eating ', str)
            this.next()
        })
        return this
    }
    sleep (n) {
        this.task.push(() => {
            setTimeout(() => {
                console.log('等待了' + n + 's')
                this.next()
            }, n)
        })
        return this
    }
    sleepFirst (n) {
        this.task.unshift(() => {
            setTimeout(() => {
                console.log('等待了' + n + 's')
                this.next()
            }, n)
        })
        return this
    }
    next () {
        let fn = this.task.shift()
        fn && fn()
    }
};

let LazyMan = function (name) {
    return new LazyManClass(name)
};

// LazyMan('Tony');
// Hi I am Tony

// LazyMan('Tony').sleep(10).eat('lunch');
// Hi I am Tony
// 等待了10秒...
// I am eating lunch

// LazyMan('Tony').eat('lunch').sleep(10).eat('dinner');
// Hi I am Tony
// I am eating lunch
// 等待了10秒...
// I am eating diner

LazyMan('Tony').eat('lunch').eat('dinner').sleepFirst(5).sleep(10).eat('junk food');
// Hi I am Tony
// 等待了5秒...
// I am eating lunch
// I am eating dinner
// 等待了10秒...
// I am eating junk food

这道题的关键点有如下几个:

chaoranfeng commented 4 years ago

@zeroone001 请问为什么每个函数要通过闭包的形式,参考@lvwxx的形式,不需要闭包也能实现,请问使用闭包是出于什么考虑么

zuotang commented 4 years ago
class MyClass{
  constructor(name){
    this.timer=null;
    this.taskList=[];
    console.log(`Hi I am ${name}`)
  }
  sleep(time){
    this.run(async ()=>{
      await new Promise(resolve=>setTimeout(resolve,time*1000))
      console.log(`等待了${time}秒...`)
    })
    return this
  }
 sleepFirst(time){
    this.run(async ()=>{
      await new Promise(resolve=>setTimeout(resolve,time*1000))
      console.log(`等待了${time}秒...`)
    },'unshift')
    return this
  }
  run(fun,act='push'){
    if(this.timer)clearTimeout(this.timer);
    this.taskList[act](fun)
    this.timer=setTimeout(this.next.bind(this));
    return this
  }

  async next(){
    let current;
    while(current=this.taskList.shift()){
       await current()
    }
  }
  eat(name){
    this.run(()=>console.log(`I am eating ${name}`))
    return this
  }

}
function LazyMan(name){
  return new MyClass(name)
}
yesixuan commented 4 years ago
const LazyManClass = class {
  constructor(name) {
    console.log('Hi I am ' + name)
    this.isPendding = false
    this.sleepCb = [[]]
    this.sleeping = Promise.resolve()
  }
  get len() {
    return this.sleepCb.length
  }
  sleepFirst(time) {
    this.sleep(time)
    return this
  }
  sleep(time) {
    if (this.isPendding) {
      this.sleepCb[this.len - 1].push(() => this.sleep(time))
    } else {
      this.sleepCb.push([])
      const mySleepCb = this.sleepCb[this.len - 1]
      this.sleeping = new Promise(resolve => {
        this.isPendding = true
        setTimeout(() => {
          console.log(`等待了${time}秒...`)
          this.isPendding = false
          resolve()
        }, time * 1000)
      })
      this.sleeping.then(() => {
        mySleepCb.forEach(fun => fun.call(this))
      })
    }
    return this
  }
  eat(food) {
    if (this.isPendding) {
      this.sleepCb[this.len - 1].push(() => this.eat(food))
    } else {
      console.log('Hi I eating  ' + food)
    }
    return this
  }
}

const LazyMan = name => new LazyManClass(name)
wangMengLiang commented 4 years ago
class LazyManClass {
  constructor(name){
    this.name = name
    this.queue = [] //队列
    console.log(`Hi I am ${this.name}`)
    setTimeout(() => {
      this.nextTask()
    }, 0);
  }

  sleep(time){
    let task = () => {
      setTimeout(() => {
        console.log(`等待了${time}秒...`)
        this.nextTask()
      }, time*1000);
    }
    this.queue.push(task)
    return this
  }

  sleepFirst(time){
    let task = () => {
      setTimeout(() => {
        console.log(`等待了${time}秒...`)
        this.nextTask()
      }, time*1000);
    }
    this.queue.splice(0, 0, task)
    return this
  }

  eat(food){
    const task = () => {
      console.log(`I am eating ${food}`)
      this.nextTask()
    }
    this.queue.push(task)
    return this
  }
  //下一任务
  nextTask(){
    let task = this.queue.shift()
    task && task()
  }
}

function LazyMan(name){
  return new LazyManClass(name)
}