如何在闭包中的setInterval中处理setTimeout

时间:2017-04-25 00:34:01

标签: javascript closures settimeout

function doSth(fn) {
  // only code here
}

const fn = doSth(()=>{
  console.log('hello world');
})

setInterval(() => {
  fn()
},10)

此功能需要输出" hello world"每秒一次,我试图在封闭内部使用类似功能节流的东西,但它没有像我预期的那样工作:

  function doSth(fn) {

    return function () {
      clearTimeout(fn.tid)
      fn.tid = setTimeout(function () {
        fn()
      },1000)
    }
  }

  const fn = doSth(()=>{
    console.log('hello world');
  })

  setInterval(() => {
    fn()
  },10)

我预计每1秒会有一个setTimeout函数,额外的调用将被取消,但它似乎很混乱。是因为doSth函数内部的闭包还是我使用setTimeout的方式?

3 个答案:

答案 0 :(得分:1)

如果我理解正确,你似乎必须将setInterval移动到doSth(fn)函数中。

function doSth(fn) {
  setInterval(() => {
    fn();
    }, 10)
}

const fn = doSth(() => {
    console.log('hello world');
})

答案 1 :(得分:1)

使用setTimeout无法解决您的问题。这是合乎逻辑的。 setTimeout做的是延迟某事的执行。因此,如果你有setInterval每秒100次(每10毫秒),你在其中调用setTimeout,你所做的就是每秒100次调用setTimeout,这仍然会打印{{ 1}}每秒100次:

hello world

解决方案是计算您被调用的时间,并且每100次 100 times per second 100 times per second setInterval setInterval ╲ setInterval ╲╲ . ╲╲╲ 1 second later . ╲╲╲ . ╲╲╲ ╲╲╲______________ setTimeout ╲╲______________ setTimeout ╲______________ setTimeout . . 调用您的函数打印hello world。让我们从最简单的方法开始:全局变量:

setInterval

这是有效的,但我们引入了一个丑陋的全局变量。幸运的是,我们知道var counter = 0; function doSth(fn) { counter ++; if (counter >= 100) { // once per second counter = 0; fn(); } } 不会直接调用我们的函数。相反,它希望我们的函数返回另一个它将调用的函数:

setInterval

这意味着我们需要编写这样的函数:

const fn = doSth(...);

这非常幸运,因为闭包是一种允许函数共享局部变量的机制。基本上它使局部变量的行为有点像全局变量,但不会污染全局命名空间。现在我们可以简单地将全局变量移动到闭包中:

var counter = 0;

function doSth(fn) {  // <──┐
  return function(){  // <──┴─ oh look, a closure!
    counter ++;
    if (counter >= 100) { // once per second
      counter = 0;
      fn();
    }
  }
}

答案 2 :(得分:0)

如果你的问题是:

  

我预计每1秒会有setTimeout个功能

这就是答案。现在它是如何工作的?好setIntervalsetTimeout基本上会尝试做同样的事情,但对于您的案例setInterval方法将比setTimeout更准确。为什么?因为setTimeout等待1000毫秒,运行闭包然后设置另一个超时。因此等待时间实际上比1000ms多一点(或者如果您的函数需要很长时间才能执行,则会更多)。

由于JavaScript不是多线程语言,setInterval也会延迟,这意味着 - 如果脚本的其他部分正在运行 - 则间隔必须等待完成。 查看我为您制作的这个例子。

更新我将setTimeout放在setInterval内请查看。

var speed = 1000; // One second
var start = +new Date;
var icounter = 0;
var tcounter = 0;
var fnLabel = '<td>fn</td><td>';
var setIntervalLabel = '<td>setInterval</td><td>';
var etdtd = '</td><td>';
var i = document.querySelector('#i');
var t = document.querySelector('#t');

setInterval(() => {
  fn();
  var time = (new Date - start) / 1000;
  var avg = tcounter++/ time;
  t.innerHTML = setIntervalLabel + tcounter + etdtd + time.toFixed(3) + etdtd + avg.toFixed(6) + etdtd + '</td>';
}, 10)

const fn = doSth(() => {
  var time = (new Date - start) / 1000;
  var avg = icounter++/ time;
  i.innerHTML = fnLabel + icounter + etdtd + time.toFixed(3) + etdtd + avg.toFixed(6) + etdtd + 'Hello ' + icounter + '</td>';
});

function doSth(fn) {
  var counter = 0;
  return function() {
    counter++;
    if (counter > 99) { // once per second
      counter = 0;
      fn();
    }
  }
}
table,
th,
td {
  border: 1px solid black
}

th,
td {
  padding: .2em .4em .3em
}

th {
  font-weight: bold;
  background-color: #89C
}
<table>
  <tr>
    <th>type</th>
    <th>calls</th>
    <th>seconds</th>
    <th>average calls/second</th>
    <th>Say Hello world!</th>
  </tr>
  <tr id="i">
    <td>fn</td>
    <td>0</td>
    <td>0</td>
    <td>0</td>
    <td></td>
  </tr>
  <tr id="t">
    <td>setIntervalLabel</td>
    <td>0</td>
    <td>0</td>
    <td>0</td>
    <td></td>
  </tr>
</table>