求助一到笔试题,重写setInterval的方法

created() {
  this.interval = setInterval(async () => {
      const resp = await getNewStatistics();
      this.rows = resp.rows;
    }, 500);
}

destroyed() {
  clearInterval(this.interval);
}

其中getNewStatistics()是访问后端的异步操作

  1. 发现异步操作getNewStatistics()返回速度有点慢, 导致操作出现堆积, 现要求实现一个函数 setIntervalWaitable(callback, ms) 代替上述代码中的setInterval,要求异步函数callback完成后,等待ms的时间,再重新执行callback
  2. 将1中的setIntervalWaitable函数的行为修改为如下方式该怎么实现:
  • 等待传入的ms时间,如果此时callback已经完成,重新执行callback
  • 否则,等待callback完成,之后立刻重新执行callback
  1. (选答)为上面实现的setIntervalWaitable函数提供一个可停止实现,用于代码中的destroyed()方法当中
阅读 3.1k
3 个回答

看着是个 setInterval,实际是个 setTimeout

function setIntervalWaitable(callback, ms = 0) {
  // 参数校验省略
  const fn = (async () => {
    try {
      await callback();
    } catch (err) {
      console.error(err);
    }
    
    setTimeout(fn, ms); // 递归调用
  });
  
  setTimeout(fn, ms); // 立即触发一次
}

测试结果(模拟 AJAX 请求阻塞 5 秒,上一次请求结束后等 5 秒再开始下一次,即 5+5=10):

image.png

https://codesandbox.io/s/bar-...

function getNewStatistics() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(Date.now());
    }, 2000);
  });
}

function setIntervalWaitable(callback, delay) {
  let _abortRejector;
  const internal = () => {
    const ret = Promise.race([
      Promise.all([
        callback(),
        new Promise(resolve => setTimeout(() => resolve(), delay))
      ]),
      new Promise((_, reject) => (_abortRejector = reject))
    ]);
    ret.then(
      () => internal(),
      _ => {
        if (_.message !== "abort") throw _;
      }
    );
  };
  internal();
  return () => {
    _abortRejector(new Error("abort"));
  };
}

class Foo {
  constructor() {
    this.abort = null;
    this.created();
    document.documentElement.onclick = () => this.abort();
  }

  created() {
    this.abort = setIntervalWaitable(async () => {
      console.log("begin");
      const resp = await getNewStatistics();
      console.log("end", resp);
    }, 500);
  }

  destroyed() {
    this.abort();
  }
}

new Foo();

个人理解,如果不对,还望指教哈

const setIntervalWaitable = (function() {
  let isDestroyed;
  // 销毁函数
  function destroyed() {
    isDestroyed = true;
  }
  return function _setIntervalWaitable(callback, ms, instance) {
    // 如果已经被销毁,则直接返回
    if (isDestroyed) {
      return destroyed;
    }
    const start = new Date();
    // 是否立即执行
    const time = instance ? 0 : ms;
    setTimeout(async () => {
      await callback();
      const current = new Date();
      // 计算等待的时间,如果等待已经超过了ms毫秒,则立即执行,否则,重新等待ms毫秒
      const shouldInstanceInvoke = !!(current - start >= ms);
      console.log("cost", current - start);
      _setIntervalWaitable(callback, ms, shouldInstanceInvoke);
    }, time);
    return destroyed;
  };
})();
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题