如何在每个调用堆栈中只调用一次函数?
How to invoke a function only once per call stack?
假设我有一个服务器,每当客户端的数据发生变化时,它就会向其客户端发送数据。客户端 class 看起来像这样:
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
,例如 1
或 2
,结果是不一致的 - 有时它被调用一次,有时被调用多次。
我需要这样的东西:
// logs "now" once
var magic = _.deferottle(log);
for (let i = 0; i < 10; i++) {
magic();
}
有什么办法可以实现吗?
这应该适用于对 update()
方法的多个同步调用:
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)
}
实际上您不需要 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));
};
我在洗澡的时候突然想到了一个解决办法:
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>
假设我有一个服务器,每当客户端的数据发生变化时,它就会向其客户端发送数据。客户端 class 看起来像这样:
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
,例如 1
或 2
,结果是不一致的 - 有时它被调用一次,有时被调用多次。
我需要这样的东西:
// logs "now" once
var magic = _.deferottle(log);
for (let i = 0; i < 10; i++) {
magic();
}
有什么办法可以实现吗?
这应该适用于对 update()
方法的多个同步调用:
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)
}
实际上您不需要 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));
};
我在洗澡的时候突然想到了一个解决办法:
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>