声网面试(一面)--实现异步任务链式调用

278 阅读1分钟

题目:实现一个方法,可以按顺序打印出需要的文字

function machine (name) {
}
machine('ygy').execute()
// start ygy (输出)
machine('ygy').do1('eat').execute();
// start ygy (输出)
// ygy eat (输出)
machine('ygy').wait(1).do1('eat').execute();
// start ygy (输出)
// wait 1s(等待1s)
// ygy eat (输出)
machine('ygy').waitFirst(5).do1('eat').execute();
// wait 5s (等待5s)
// start ygy (输出)
// ygy eat  (输出)

这是一个典型的链式调用,如果不考虑异步的任务的话,只需要在每个方法后面返回this就可以实现链式调用。

function machine (name) {
  this.name = name
  this.do1 = function (action) {
    console.log(`${this.name} ${action}`)
    return this
  }
  this.execute = function () {
    console.log(`start ${this.name}`)
    return this
  }
  return this
}
// 这样可以实现链式调用但是顺序不对
machine('ygy').do1('eat').execute();

// ygy eat (输出)
// start ygy (输出)

观察一下别的输出要求,每个execute都是最后调用,但是需要最先打印,我想到的解决方法是用一个变量存储一下是否已经打印了 start

function machine (name) {
  this.name = name
  this.started = false

  this.doStart = function () {
    if (!this.started) {
      console.log(`start ${this.name}`)
      this.started = true
    }
  }
  this.do1 = function (action) {
    this.doStart()
    console.log(`${this.name} ${action}`)
    return this
  }
  this.execute = function () {
    this.doStart()
    return this
  }
  return this
}
// 这样打印的顺序就是对的了
machine('ygy').do1('eat').execute();

// start ygy (输出)
// ygy eat (输出)

接下来就是异步的任务,它需要我们自己实现一个事件栈,在任务执行完之后再执行下一个任务。

function machine (name) {
  this.name = name
  this.started = false
  this.actions = []

  this.next = function () {
    this.doStart()
    // 先进先出
    let fn = this.actions.shift()
    fn && fn()
  }

  this.doStart = function () {
    if (!this.started) {
      console.log(`start ${this.name}`)
      this.started = true
    }
  }
  this.do1 = function (action) {
    // 讲要执行的函数推入事件栈,并且在函数中调用next 
    this.actions.push(() => {
      console.log(`${this.name} ${action}`)
      this.next()
    })
    return this
  }
  this.wait = function (sec) {
    // 讲要执行的函数推入事件栈,并且在函数中调用next 
    this.actions.push(() => {
      setTimeout(() => {
        console.log(`wait ${sec}s`)
        this.next()
      }, sec * 1000);
    })
    return this
  }
  this.execute = function () {
    this.next()
  }
  return this
}

machine('ygy').wait(1).do1('eat').execute();
// start ygy (输出)
// wait 1s(等待1s)
// ygy eat (输出)

waitFirst 需要先等待,等待完成后再开始执行,我的想法是用started 这个变量控制打印开始的时间。

function machine (name) {
  ...
  this.waitFirst = function (sec) {
    this.started = true
    this.actions.push(() => {
      setTimeout(() => {
        console.log(`wait ${sec}s`)
        this.started = false
        this.next()
      }, sec * 1000);
    })
    return this
  }
  this.execute = function () {
    this.next()
  }
  return this
}
machine('ygy').waitFirst(5).do1('eat').execute();
// wait 5s (等待5s)
// start ygy (输出)
// ygy eat  (输出)

完整代码

function machine (name) {
  this.name = name
  this.started = false
  this.actions = []

  this.next = function () {
    this.doStart()
    // 先进先出
    let fn = this.actions.shift()
    fn && fn()
  }

  this.doStart = function () {
    if (!this.started) {
      console.log(`start ${this.name}`)
      this.started = true
    }
  }
  this.do1 = function (action) {
    this.actions.push(() => {
      console.log(`${this.name} ${action}`)
      this.next()
    })
    return this
  }
  this.wait = function (sec) {
    this.actions.push(() => {
      setTimeout(() => {
        console.log(`wait ${sec}s`)
        this.next()
      }, sec * 1000);
    })
    return this
  }
  this.waitFirst = function (sec) {
    this.started = true
    this.actions.push(() => {
      setTimeout(() => {
        console.log(`wait ${sec}s`)
        this.started = false
        this.next()
      }, sec * 1000);
    })
    return this
  }
  this.execute = function () {
    this.next()
  }
  return this
}

由于在面试中过于紧张导致这道题并没有做出来😅,有没有小伙伴对链式调用感兴趣可以讨论一下