如何在每个调用堆栈中只调用一次函数?

时间:2017-02-27 19:25:01

标签: javascript underscore.js callstack throttling

假设我有一台服务器,每次客户端的数据发生变化时都会向其客户端发送数据。客户端类看起来像这样:

function Client() {
    this.data = {};
    this.update = function (key, value) {
        this.data[key] = value;
        this.emitUpdate();
    };

    this.emitUpdate = function () {
        // tell server to send this client's data
    };
}

var myClient = new Client();

如果我改变了一件事:

myClient.update("name", "John");

服务器使用此客户端的新信息更新所有客户端。大。

但是...

如果我从我的应用中的不同位置一次更改多个内容:

if (something === true) {
    myClient.update("something", true);
} else {
    myClient.update("something_else", true);
}

myClient.update("age", Date.now());

总会有两件事发生变化,emitUpdate()会被调用两次。服务器将发送两次数据,所有客户端都必须渲染两次。想象一下100次发生的变化......考虑到服务器只发送一次更新,这将是一个很大的开销,因为所有这些变化都发生在一个时间范围内。

如何在每个调用堆栈中仅调用emitUpdate()一次?我使用underscore.js并检查了defer()throttle()函数。问题是,我需要将它们的效果结合起来。

var log = function () {
  console.log("now");
};

// logs "now" 10 times
for (let i = 0; i < 10; i++) {
  _.defer(log);
}

// logs "now" 10 times
var throttled = _.throttle(log, 0);
for (let i = 0; i < 10; i++) {
  throttled();
}

如果throttle()使用wait 12// logs "now" once var magic = _.deferottle(log); for (let i = 0; i < 10; i++) { magic(); } ,则结果不一致 - 有时会调用一次,有时会更多。

我需要这样的东西:

# combine the time series actual and forcasted values
combined <- cbind(predicted, actual=ldeaths)

# plot the different values as different series    
dygraph(combined , main = "Predicted Lung Deaths (UK)") %>%
  dyAxis("x", drawGrid = FALSE) %>%
  dySeries("actual", label = "actual") %>%
  dySeries(paste0("predicted.", c("lwr", "fit", "upr")), label = "Predicted") %>%
  dyOptions(colors = RColorBrewer::brewer.pal(3, "Set1"))

有没有办法实现呢?

3 个答案:

答案 0 :(得分:2)

这适用于update()方法的多个同步调用:

&#13;
&#13;
function Client() {
  this.data = {};
  this.update = function (key, value) {
    this.data[key] = value;
    if (!this.aboutToUpdate) {
      this.aboutToUpdate = setTimeout(() => {
        this.aboutToUpdate = false
        this.emitUpdate()
      })
    }
  };

  this.emitUpdate = function () {
    // tell server to send this client's data
    console.log('update sent', this.data)
  };
}

// This should only log 'update sent' once
var client = new Client()
for (var i = 0; i < 5; i++) {
  client.update(i, i)
}
&#13;
&#13;
&#13;

答案 1 :(得分:2)

实际上你不需要throttle,只需将defer与一些布尔标志检查结合使用。

这样的事情:

this.update = function (key, value) {
    this.data[key] = value;
    if (this.isQueued) return;
    this.isQueued = true;
    _.defer(function () {
        this.isQueued = false;
        this.emitUpdate();
    }.bind(this));
};

答案 2 :(得分:0)

洗澡时脑袋里浮现出一种解决方案:

var deferottle = function(func) {
  var called, args, ctx;

  return function() {
    args = arguments;
    ctx = this;

    if (!called) {
      called = true;
      setTimeout(function() {
        func.apply(ctx, args);
        called = false;
      });
    }
  };
};

var magic = deferottle(function(num) {
  console.log("num: ", num);
});

for (let i = 0; i < 10; i++) {
  magic(i);
}

结果debounce()等待0也可以使用:

var magic = _.debounce(function(num) {
  console.log("num: ", num);
}, 0);

for (let i = 0; i < 10; i++) {
  magic(i);
}
<script src="http://underscorejs.org/underscore-min.js"></script>