I'm trying to debounce a save function that takes the object to be saved as a parameter for an auto-save that fires on keystroke. The debounce stops the save from happening until the user stops typing, or at least that's the idea. Something like:
var save = _.debounce(function(obj) {
...
}, delay);
Where this falls apart is if I try to save two objects in quick succession. Because the debounce doesn't take the passed in object into account, only the second call to save will fire and only one object will be saved.
save(obj1);
save(obj2);
Will only save obj2, for example.
I could make obj an instance of a class that has its own save method that takes care of debouncing saves to just that object. Or keep a list of partial/curried functions somewhere, but I'm hoping there's a one stop solution out there. Something like:
var save = _.easySolution(function(obj) {
...
}, delay);
What I'm looking for the following string of saves to save each object, but only save each object once.
save(obj1);
save(obj2);
save(obj3);
save(obj2);
save(obj2);
save(obj3);
save(obj2);
save(obj1);
EDIT: Something like this, maybe, just not so convoluted, and something that doesn't mutate the obj with a __save function?
function save(obj) {
if(!obj.__save) {
obj.__save = _.debounce(_.partial(function(obj) {
...
}, obj), delay);
}
obj.__save();
}
You're going to want to create a debounced version of the function for each argument that get's passed. You can do this fairly easily using debounce(), memoize(), and wrap():
function save(obj) {
console.log('saving', obj.name);
}
const saveDebounced = _.wrap(
_.memoize(() => _.debounce(save), _.property('id')),
(getMemoizedFunc, obj) => getMemoizedFunc(obj)(obj)
)
saveDebounced({ id: 1, name: 'Jim' });
saveDebounced({ id: 2, name: 'Jane' });
saveDebounced({ id: 1, name: 'James' });
// → saving James
// → saving Jane
You can see that 'Jim' isn't saved because an object with the same ID is saved right after. The saveDebounced() function is broken down as follows.
The call to _memoize() is caching the debounced function based on some resolver function. In this example, we're simply basing it on the id property. So now we have a way to get the debounced version of save() for any given argument. This is the most important part, because debounce() has all kinds of internal state, and so we need a unique instance of this function for any argument that might get passed to save().
We're using wrap() to invoke the cached function (or creating it then caching it if it's the first call), and pass the function our object. The resulting saveDebounced() function has the exact same signature as save(). The difference is that saveDebounced() will debounce calls based on the argument.
Note: if you want to use this in a more generic way, you can use this helper function:
const debounceByParam = (targetFunc, resolver, ...debounceParams) =>
_.wrap(
_.memoize(
() => _.debounce(targetFunc, ...debounceParams),
resolver
),
(getMemoizedFunc, ...params) =>
getMemoizedFunc(...params)(...params)
)
// And use it like so
function save(obj) {
console.log('saving', obj.name);
}
const saveDebounced = debounceByParam(save, _.property('id'), 100)
Maybe something like:
var previouslySeen = {}
var save = _.debounce(function(obj) {
var key = JSON.stringify(obj);
if (!previouslySeen[key]) {
previouslySeen[key] = 1;
} else {
...
}
}, delay);
You can use internal object in closure for set/get debounced function.
In this example we check if debounced function with this args alredy saved in our memory object while call debounced function. If no - we create it.
const getDebouncedByType = (func, wait, options) => {
const memory = {};
return (...args) => {
// use first argument as a key
// its possible to use all args as a key - e.g JSON.stringify(args) or hash(args)
const [type] = args;
if (typeof memory[searchType] === 'function') {
return memory[searchType](...args);
}
memory[searchType] = debounce(func, wait, { ...options, leading: true }); // leading required for return promise
return memory[searchType](...args);
};
};
Original gist - https://gist.github.com/nzvtrk/1a444cdf6a86a5a6e6d6a34f0db19065
Related
This is one of those job interview tests on HackerRank that I am embarrassed to say I didn't complete within the allotted 20 minutes, and it's driving me nuts. Basically, you're given the following:
function myList() {
// write code here
}
function main() {
// You can't edit this function. This is just pseudo, from what I can remember
const obj = myList();
if (operation === "add")
obj.add(item);
else if (operation === "remove")
obj.remove(item);
else
obj.getList();
// Some more stuff here
}
So my first train of thought was to write the following inside myList():
let objList = [];
function add(item) {
objList.add(item)
}
// etc., etc.
return {add, remove, getList};
Needless to say, the above didn't work, with the error: obj.add is not a function, in the main function.
I tried experimenting with this and the returns (only returning add, with or without {}), but didn't really get anywhere. What could I have done to get this working?
As the answer from Attersson already suggested, you could use a closure inside the myList function. This would allow to only expose the three methods add, remove and getList while the original list cannot be accessed. Here is an example:
function myList() {
return (() => {
const obj = {itemList: []}
const add = (item) => obj.itemList.push(item)
const remove = (item) => obj.itemList = obj.itemList.filter(i => i !== item)
const getList = () => obj.itemList
return {add, remove, getList}
})()
}
For obj, inside myList() you could have run a closure, that is a self invoking function returning, in this case, an object with your k:v exports, but with private access to the context of the now expired anonymous caller. With "const add = function" etc statements you can define values for your exports, while the objList goes in the private context.
So I am calling a function that calls lodash's once function:
if (isPageTwo) {
sendSegmentData(sendEnhancedTrackEvent);
}
And I have the functions defined here:
const pageTwoSegmentEvent = (sendEnhancedTrackEvent) => {
const enhanceableData = {
name: 'Page loaded',
properties: {
...defaultProps,
cid: getCid(),
epid: getEpid(),
name: 'ReviewExperienceModernDoubleStep'
}
};
sendEnhancedTrackEvent(enhanceableData);
}
const sendSegmentData = (sendEnhancedTrackEvent) => {
once(() => {
pageTwoSegmentEvent(sendEnhancedTrackEvent);
});
}
I am trying to pass the sendEnhancedTrackEvent callback function to the pageTwoSegmentEvent function but I guess the way I'm trying to pass it through the once function pageTwoSegmentEvent never gets called. Does anyone know how to do this?
The _.once() method takes a function (func), and returns a function that invokes the wrapped function (func) a single time. According to the docs:
The func is invoked with the this binding and arguments of the created
function.
Which means that whatever arguments you pass to the new function, will be passed to the wrapped func.
In your case:
sendSegmentData has the sendEnhancedTrackEvent param
When sendSegmentData is invoked, it calls once(() => { pageTwoSegmentEvent(sendEnhancedTrackEvent); });, which creates a new function. The new function is not returned or called.
To create sendSegmentData, call once on pageTwoSegmentEvent directly. This will return a new function, that will pass whatever arguments in gets to pageTwoSegmentEvent.
Example:
const { once } = _
const pageTwoSegmentEvent = (sendEnhancedTrackEvent) => console.log(sendEnhancedTrackEvent)
const sendSegmentData = once(pageTwoSegmentEvent)
sendSegmentData('1')
sendSegmentData('2')
sendSegmentData('3')
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
_.once returns the function that you need to invoke. No matter how many times you call this function it will only be invoked once.
Assuming once is an alias to _.once, try changing it to this:
const sendSegmentData = once((sendEnhancedTrackEvent) => {
pageTwoSegmentEvent(sendEnhancedTrackEvent);
});
...
// somewhere else call it
sendSegmentData(theSegmentedData);
I have a function from Dan Abramov`s redux course. It takes store but returning a function taking action as an argument. I know clojures but in function we didnt excute last function. addLoggingToDispatch function doesn't have action argument how does that function works?
const addLoggingToDispatch = (store) => {
const rawDispatch = store.dispatch;
return (action) => {
console.group(action.type);
console.log('%c prev state', 'color: gray', store.getState());
console.log('%c action', 'color: blue', action);
const returnValue = rawDispatch(action);
console.log('%c next state', 'color: green', store.getState());
console.groupEnd(action.type);
return returnValue;
};
};
It looks like this function is intended to replace or wrap the dispatch method that comes with your store. The dispatch method takes an action as an argument, so it has the same signature as your return function. So wherever you actually create the store, do something like this:
const store = createStore()
store.dispatch = addLoggingToStore(store)
You might clarify this post by showing us where you actually are using addLoggingToStore. But if I understand correctly this purpose of this function is to bake in logging functionality without creating any sort of additional logging middleware. Now, whenever you call dispatch(someAction), it will run your function instead of the default provided by the redux store.
The function that addLoggingToDispatch returns closes over the store parameter. Then, when that function is called, it gets passed an argument for action. So when that function that's returned is running, it has access to action (because that's one of its parameters) and store (because it closes over addLoggingToDispatch's parameter).
When a function is created inside another function and the inner function continues to exist after the outer function returns (because the outer function returns it, or it's been added to a list of functions like an event handler list, etc.), the inner function has an enduring link to the context in which it was created (the call to the outer function) and all of the in-scope parameters/variables of that context (store, in this example).
Here's a simpler example:
function createAdderFunction(a) {
return function(b) {
return a + b;
};
}
var addFive = createAdderFunction(5);
// Now, `addFive` is a function that will add 5 to whatever you call it with
console.log(addFive(2)); // 7
console.log(addFive(37)); // 42
var addTen = createAdderFunction(10);
// And `addTen` is a function that will add 10 to whatever you call it with
console.log(addTen(15)); // 25
// `addFive` and `addTen` are separate from each other and work independently
console.log(addFive(0)); // 5
console.log(addTen(0)); // 10
I have the following test code:
var async = require('async');
var GROUP = 'testGroup';
var opts = {
someKey: 'hi',
};
test(opts);
function test(options) {
async.series([
doThis.bind(null, options),
doThat.bind(null, options),
], function(results) {
debugger;
});
}
function doThis(options, cb) {
options.someKey = [GROUP, options.someKey].join('.');
return cb();
}
function doThat(options, cb) {
debugger;
options.someKey = [GROUP, options.someKey].join('.');
return cb();
}
When we hit the debugger in doThat(), options.someKey already has the value someGROUP.hi, so when the function finishes we end up with options.someKey === 'someGROUP.someGroup.hi'
How do we bind such that the original object does not change? The bind is necessary because we need to pass in options to the functions that run within async.series. Otherwise, we could just invoke the functions and pass in the object as a parameter.
I'm don't think your partially applying the options parameter to your doThis(), doThat() functions is especially pertinent.
You're passing the same javascript object/literal as aparameter to two functions and and then mutate that parameter.
If you don't to mutate that object then don't. Find some other way of returning the results of your operation. doThis() and doThat() could return values instead of modifying the parameter. You could gather them up in the final callback after the series gets called.
If you just want to preserve the intital value of opts, use lodash or something to make a deep clone of opts before you pass it into test.
This question in summary is to figure out how to pass variables between javascript functions without: returning variables, passing parameters between primary functions, using global variables, and forcing function 1 to wait for function 2 to finish. I figured out a jQuery solution and posted in below (in the answers section).
Old Post: I initialize a set of four functions, each calling on each other in a different way. At the end of it, I need the final modified product (an array) returned to the initializing function.
Global variables don't force the initial function to wait. And returning it backwards four times doesn't work either. How do you pass a modified variable back to its initializing function, if you can't return it? Or why isn't it returning?
(the maze starts at initFunctionA, ends at functionD)
classOne = {
initFunctionA : function() {
classTwo.functionB(functionD, array);
// I NEED ACCESS TO ARRAY2 HERE
},
functionD : function(data, array) {
var array2 = // modifications to array
}
}
{...}
classTwo = {
functionB : function(callback, array) {
$.ajax({
success: function(ret){
classTwo.functionC(ret, callback, array)
}
});
},
functionC : function(ret, callback, array) {
callback(ret.data.x, array);
}
}
Change your callback (at the call site) such that you capture the return value of functionD. Then, change functionD so that it returns array2. I've added this access to the example below as a convenience. (Also, be sure to include semicolons where "required" if you want to make JSLint happy.)
classOne = {
initFunctionA : function() {
var self = this;
classTwo.functionB(function() {
var array2 = functionD.apply(self, arguments);
// ACCESS ARRAY2 HERE
}, array);
},
functionD : function(data, array) {
var array2 = // modifications to array
return array2;
}
};
{...}
classTwo = {
functionB : function(callback, array) {
$.ajax({
success: function(ret){
classTwo.functionC(ret, callback, array)
}
});
},
functionC : function(ret, callback, array) {
callback(ret.data.x, array);
}
};
You can't make it work with a pattern like you've written there; it's simply not possible in Javascript because there's no such thing as "waiting". Your ajax code has to take a callback parameter (which you've got, though it's not clear where it comes from or what it does), and that initial function should pass in code to do what you need with the array after the ajax call finishes.
I would use an object constructor:
function ClassOne() {
this.array2 = [];
}
ClassOne.prototype.initFunctionA = function() {
// ...
}
ClassOne.prototype.functionD = function(data, array) {
// Can use array2 EX: this.array2
}
var classOne = new ClassOne();
This is how I understand your problem:
classTwo handles an AJAX call and may modify the result. classOne makes use of classTwo to get some data and needs the resulting data.
If so, how's this:
classOne = {
initFunctionA : function() {
var array = ['a','b','c'];
classTwo.functionB(this.functionD, array);
},
functionD : function(data, array) {
// This function is called when the AJAX returns.
var array2 = // modifications to array
}
}
{...}
classTwo = {
functionB : function(callback, array) {
$.ajax({
success: function(ret){
classTwo.functionC(ret, callback, array)
}
});
},
functionC : function(ret, callback, array) {
callback(ret.data.x, array);
}
}
So classOne.initFunctionA calls classTwo.functionB which sets up an ajax call. When the ajax call completes successfully, classTwo.functionC is called with the result and the initial array. From here, classOne.functionD is called with ret.data.x and the array.
Okay! I found a way to pass variables between functions without:
making global variables
making object properties (Chaos's solution)
passing parameters
These three were suggested here as the only ways.
Accessing variables from other functions without using global variables
But, if you you can't pass parameters directly, and you need one function to wait for the other (i.e, can't rely on references), and you're using asynchronous calls to the server in an intermediate function, then your only solution is:
Using jQuery...
Create this object in the DOM (dynamically if you don't want to muddy your markup):
<div id='#domJSHandler" style="display: none;"></div>
Then in the function that must wait:
//Function & Class Set 2
$('#domJSHandler').bind('proceedWithAction', function(event, param1, param2) {
// action set 2
});
And in the function to be waited on:
//Function & Class Set 1
// action set 1
$('#domJSHandler').triggerHandler('proceedWithAction', [param1, param2]);
Essentially encase the last actions you need to perform in a jQuery bind custom event on an invisible DOM object. Trigger that event from JS with jQuery's triggerHandler. Pass your parameters and voila!
I'm sure SO will give me crap for this (and for narcissistically accepting my own answer) but I think it's pretty brilliant for a uber-newbie and it worked for me.
So :p Stack Overflow
(jk You've all saved my ass many times and I love you all :)