OK, let's keep it simple, I have the following :
function loopDaLoop(){
for (var i = 0; i < tempItemsLength; i++) {
var product = tempItems[i];
dust.render('product', product, addProductOrFinish);
}
}
and i'd like to get the current value of i inside my callback function
function addProductOrFinish(err, out) {
console.log(i); // undefined
}
I do know it's simple, really I do... Help?
Edit :
I know I'm supposed to use a closure so I tried and failed with this :
(function(i){
dust.render('product', product, addProductOrFinish);
};(i)
There are several different structures that could be used to solve this problem. The simplest is to use .bind() to add the desired parameter to the function call.
function loopDaLoop(){
for (var i = 0; i < tempItemsLength; i++) {
var product = tempItems[i];
dust.render('product', product, addProductOrFinish.bind(null, i));
}
}
function addProductOrFinish(i, err, out) {
console.log(i);
}
This will cause the value of this to change in addProductOrFinish if that was important. If so, you could work around that too, but it's not as simple.
Here's another approach using a closure that returns a function and preserves the value of this in case dust.render() is setting that:
function loopDaLoop(){
for (var i = 0; i < tempItemsLength; i++) {
var product = tempItems[i];
dust.render('product', product, getAddProductOrFinish(i));
}
}
function getAddProductOrFinish(loopArg) {
return function(err, out) {
return addProductOrFinish.call(this, loopArg, err, out);
}
}
function addProductOrFinish(i, err, out) {
console.log(i);
}
Or, if addProductOrFinish can be an inline function, then it can use the IIFE-type structure you tried like this:
function loopDaLoop(){
for (var i = 0; i < tempItemsLength; i++) {
var product = tempItems[i];
(function(i) {
dust.render('product', product, function(err, out) {
console.log(i);
// rest of your addProductOrFinish logic here
});
)(i);
}
}
Related
I have a datatable, a checkbox on each table, and a button that will trigger my operation on that row. I would like to remove that row when my operation is done.
for (i = 0; i < checkedBoxes.length; i++) {
var chk = checkedBoxes[i];
var tdef = chk.closest("tr").querySelectorAll('td');
var myThing = tdef[1].innerHTML;
service.doSomething(myThing, function (result) {
service.doSomethingElse();
// I would like to remove this row once I'm done with this row
//browseDataTable.row($(chk).parents('tr')).remove().draw();
});
}
I know that I'm not supposed to remove that row as I'm looping through it. So I'm planning to just collect the index of each row, and when everything is finished, I can remove it, like this:
var myArr = new Array();
for (i = 0; i < checkedBoxes.length; i++) {
service.doSomething(myThing, function (result) {
service.doSomethingElse();
myArr.push(i);
}) // Chrome said 'then' is undefined, so how do I chain callback here?
.then(function () {
// Remove all rows at index in myArr
});
}
The service isn't async service, it's an ASMX service.
You are using your service both like a function with a callback and a Promise. So which is it? Does it take a callback, or does it return a Promise?
It looks like it does not return a Promise, because you are trying to chain .then() and it's undefined.
The service isn't async then why are you giving it a callback and trying to chain a .then(), if it's synchronous?
Anyway, one easy way to solve your issue is to use let, which will create a scope for every loop.
Currently :
for (i = 0; i < checkedBoxes.length; i++) { // i is a global (window) variable, that's bad
service.doSomething(myThing, function (result) {
service.doSomethingElse();
myArr.push(i); // i will always be checkboxes.length
})
}
By using let :
for (let i = 0; i < checkedBoxes.length; i++) { // i is in the local scope
service.doSomething(myThing, function (result) {
service.doSomethingElse();
myArr.push(i); // the value of i will be different (correct) each time
})
}
Say I have this function:
function doSomething(n) {
for (var i = 0; i < n; i++) {
doSomethingElse();
}
}
How would I test if the doSomethingElse function is called n times??
I tried something like:
test("Testing something", function () {
var spy = sinon.spy(doSomethingElse);
doSomething(12);
equal(spy.callCount, 12, "doSomethingElse is called 12 times");
});
but this does not seem to work, because you have to call the spy while the doSomething() calls the original doSomethingElse(). How can I make this work with QUnit/sinon.js?
EDIT
Maybe it isn't even a good idea? Does this fall outside the 'unit testing' because another function is called?
You could do something like this:
test('example1', function () {
var originalDoSomethingElse = doSomethingElse;
doSomethingElse = sinon.spy(doSomethingElse);
doSomething(12);
strictEqual(doSomethingElse.callCount, 12);
doSomethingElse = originalDoSomethingElse;
});
For example: JSFiddle.
function doSomething(n) {
for (var i = 0; i < n; i++) {
doSomethingElse();
}
}
you cant spy on doSomethingElse.
doSomethingElse is not testable ,when something is not testable it needs to be refactored.
You either need to inject doSomethingElse in doSomething
OR
use a pointer:
pointer={doSomethingElse:function(){}};
function doSomething(n) {
for (var i = 0; i < n; i++) {
pointer.doSomethingElse();
}
}
Declare a global variable named count and assign it 0
window.count = 0;
Now, inside the doSomethingElse() function, increment it like count++
So, whenever you access count variable, it will return the number of times the doSomethingElse() is called.
Full code might be:
window.count = 0;
function doSomething(n) {
for (var i = 0; i < n; i++) {
doSomethingElse();
}
}
function doSomethingElse() {
count++;
// do something here
}
doSomething(22);
alert(count);// alerts 22
Or even better, call count++ whenever the function you want to be tested is called in code.
Demo: http://jsfiddle.net/583ZJ/
Note: If you want to remove it, then just remove the variable declaration (window.count=0;) and count++
function debugCalls(f) {
if (!f.count)
f.count = 0;
f.count++;
}
function doSomethingElse()
{
debugCalls(arguments.callee);
// function code...
}
// usage
for(var i = 0; i < 100; i++) doSomethingElse();
alert(doSomethingElse.count);
this way it makes it easier for you to debug any function you want just by inserting debugCalls(arguments.callee) inside the function you want to save the number of times it has been called.
In Node.js 14.2.0 one can use the new currently experimental CallTracker API to do the job without using Sinon or another additional library.
var assert = require('assert');
test("Testing something", function () {
var originalDoSomethingElse = doSomethingElse;
var tracker = new assert.CallTracker();
doSomethingElse = tracker.calls(doSomethingElse, 12);
try {
doSomething(12);
tracker.verify();
} finally {
doSomethingElse = originalDoSomethingElse;
}
});
I've got some javascript for {} loops which I use repeatedly throughout the project, they are all similar to this:
for (var i = 0; i < things.length; i++) {
console.log(things[i]);
// This may be different in different areas of the project
}
I minified the code, but the loops take up a lot of the minified code. Is there a way to shorten the above code to something like this:
loop {
console.log(things[i]);
// This may be different in different areas of the project
}
Probably not the above, but you get the idea.
Any help would be much appreciated :)
If you are repeatedly printing different arrays, you could make a function for it to cut your repetition down:
function printArray(arr) {
for (var i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
}
then call like:
printArray(things);
If you are doing more than just printing and want it to be more universal, you should use a callback, like this:
function loopArr(arr, cb) {
for (var i = 0; i < arr.length; i++) {
cb(arr[i]);
}
}
and this could be called like:
loopArr(thing, function (i) {
console.log(i);
});
Fiddle
Also there are tools that can already do this for you, for instance if you are using (or would want to use) jQuery, you could use jQuery.each()
A jQuery-ish way to do that:
function each(arr, func) {
for ( var i = 0; i < arr.length; ++i ) {
func(arr[i]);
}
}
can be called like:
each( things, function(thing) { console.log(thing); } );
or
each( things, function(thing) { console.log(thing); alert(thing); } );
etc.
You'd have to pass in the item and the callback, but of course it's possible.
function loop (item, callback) {
for (var i = 0; i < item.length; i++) {
callback(item[i]);
}
}
Useage:
loop(things, function (item) {
console.log('do things here with each ' + item);
});
Also note that in more modern browsers you could simply do:
things.forEach(function (item) {
/* do whatever */
});
function forEach(array, func) {
var i = array.length;
while(i--) {
func(array[i]);
}
}
Everyone beat my to it, but here is another way to write it
function forEach(collection, callback){
var e;
for (var i = 0; e = collection[i++];) {
callback(e);
}
}
And its' usage:
var a = ["The", "Brown", "Cow"];
forEach(a, function(e) { console.log(e); });
Should be mentioned that there are tons of implementations of iterator functions. Your exact case my need to improve upon these.
I'm adding an event listener to some elements I'm looping through and need a closure in order to preserve the index in the event function.
<button>solution 1</button>
<button>solution 2</button>
<script>
var buttons = document.getElementsByTagName('button');
for (var i = 0; i < 3; i++) {
var log = (function closure(number) {
return function () {
console.log(number);
};
})(i);
buttons[0].addEventListener("click", log);
}
for (var i = 0, len = 3; i < len; i++) {
(function (i) {
var log = function () {
console.log(i);
};
buttons[1].addEventListener("click", log);
})(i);
}
</script>
http://jsfiddle.net/paptd/11/
Both these solutions output 0, 1, 2 correctly (try 'wrong' to see what happens without a closure) but I'm trying to understand which one I should use and why.
Which way is the correct way of doing it?
The first one works because you are defining a closure, returning a function from it, then assigning that function to a listener.
The second one seems more proper, since the closure encompasses the entire loop content, making it more obvious that the value of i is to be "locked" there.
You shouldn't use any of these--you're creating n identical functions inside of your loop. You should refactor your code into a named function that returns the event handler:
var buttons = document.getElementsByTagName('button');
function createHandler(number) {
return function () {
console.log(number);
};
}
for (var i = 0; i < 3; i++) {
buttons[0].addEventListener("click", createHandler(i));
}
Example: http://jsfiddle.net/paptd/12/
I have a helper function which allows me to call functions in a different context. It's pretty simple:
function delegate(that, thatMethod)
{
return function() { return thatMethod.apply(that,arguments); }
}
This is ok if I wan't evaluate the variables at execution of the function, but sometimes I want to give the delegate-function values which are fixed at construction time.
Sample:
var callbacks = new Array();
for(var i = 0; i < 5; i++)
{
callbacks.push(delegate(window, function() { alert(i) }));
}
callbacks[3]();
In this case my expected behavior is that I get an alert(3) but because i is evaluated at execution we don't.
I know there is another delegate function which looks something like:
function delegatedd( that, thatMethod )
{
if(arguments.length > 2)
{
var _params = [];
for(var n = 2; n < arguments.length; ++n)
_params.push(arguments[n]);
return function() { return thatMethod.apply(that,_params); }
}
else
return function() { return thatMethod.call(that); }
}
But that doesn't help me either because I want to mix both methods. It can be written like that (first version of delegate used):
function(foo) {
return delegate(window, function() {
alert(foo);
});
}(i)
So i is construction time and everything else execution time.
The disadvatage of this is that it looks pretty ugly. Is there a better way to do it? Can I somehow hide it in a function?
Thanks
You can use the bind function:
var callbacks = new Array();
for(var i = 0; i < 5; i++)
{
//callbacks.push(delegate(window, function() { alert(i) }));
callbacks.push(function(n) { alert(n) }.bind(window, i);
}
callbacks[3]();
But bind is not implemented on IE(don't know about IE9), for how get it to work on IE see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind#Compatibility.